changeset 23513:cab5420c3879

merge of '5033650a1e57437cc3f2894adc41935daa6b3269' and '95fe62719d362e8857a89e906a9008305e31dd5e'
author Elliott Sales de Andrade <qulogic@pidgin.im>
date Wed, 09 Jul 2008 00:32:18 +0000
parents 1061a9ad1ddb (current diff) f1ff35d14a95 (diff)
children 7e16d193bb57
files libpurple/protocols/msn/slp.c libpurple/protocols/msn/soap2.c libpurple/protocols/msn/soap2.h libpurple/protocols/qq/qq_proxy.c libpurple/protocols/qq/qq_proxy.h libpurple/protocols/qq/recv_core.c libpurple/protocols/qq/recv_core.h libpurple/protocols/qq/send_core.c libpurple/protocols/qq/send_core.h libpurple/protocols/qq/sendqueue.c libpurple/protocols/qq/sendqueue.h libpurple/protocols/qq/udp_proxy_s5.c libpurple/protocols/qq/udp_proxy_s5.h
diffstat 92 files changed, 4666 insertions(+), 5604 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Wed Jul 09 00:27:44 2008 +0000
+++ b/ChangeLog	Wed Jul 09 00:32:18 2008 +0000
@@ -5,8 +5,6 @@
 	* Ability to create custom smileys (currently only the MSN protocol
 	  utilizes the feature). (Thanks to Mauro Sérgio Ferreira Brasil,
 	  Marcus Lundblad, Jorge Villaseñor and other contributors)
-	* Yahoo! Japan now uses UTF-8, matching the behavior of official clients
-	  and restoring compatibility with the web messenger (Yusuke Odate)
 	* Add a configure option, --with-system-ssl-certs to allow packagers
 	  to specify a system-wide SSL CA certificates directory.  When set,
 	  we don't install our SSL CA certs, so it's important that the
@@ -31,6 +29,12 @@
 	* Added '/msgcolor' command to change colors of different classes of
 	  messages in a conversation. See '/help msgcolor' for details.
 
+version 2.4.3 (??/??/2008):
+	libpurple:
+	* Yahoo! Japan now uses UTF-8, matching the behavior of official clients
+	  and restoring compatibility with the web messenger (Yusuke Odate)
+	* Setting your buddy icon once again works for Yahoo! accounts.
+
 version 2.4.2 (05/17/2008):
 	libpurple:
 	* In MySpaceIM, messages from spambots are discarded (Justin Williams)
--- a/libpurple/idle.c	Wed Jul 09 00:27:44 2008 +0000
+++ b/libpurple/idle.c	Wed Jul 09 00:32:18 2008 +0000
@@ -252,7 +252,7 @@
 	PurpleAccount *account;
 
 	account = purple_connection_get_account(gc);
-	idled_accts = g_list_remove(idled_accts, account);
+	set_account_unidle(account);
 }
 
 static void
--- a/libpurple/internal.h	Wed Jul 09 00:27:44 2008 +0000
+++ b/libpurple/internal.h	Wed Jul 09 00:32:18 2008 +0000
@@ -140,6 +140,14 @@
 #	define G_MAXUINT32 ((guint32) 0xffffffff)
 #endif
 
+#ifndef G_MAXSIZE
+#	if GLIB_SIZEOF_LONG == 8
+#		define G_MAXSIZE ((gsize) 0xffffffffffffffff)
+#	else
+#		define G_MAXSIZE ((gsize) 0xffffffff)
+#	endif
+#endif
+
 #if GLIB_CHECK_VERSION(2,6,0)
 #	include <glib/gstdio.h>
 #endif
--- a/libpurple/protocols/msn/Makefile.am	Wed Jul 09 00:27:44 2008 +0000
+++ b/libpurple/protocols/msn/Makefile.am	Wed Jul 09 00:32:18 2008 +0000
@@ -50,10 +50,8 @@
 	slpmsg.h \
 	slpsession.c \
 	slpsession.h \
-	soap.c\
-	soap.h\
-	soap2.c \
-	soap2.h \
+	soap.c \
+	soap.h \
 	state.c \
 	state.h \
 	switchboard.c \
--- a/libpurple/protocols/msn/contact.c	Wed Jul 09 00:27:44 2008 +0000
+++ b/libpurple/protocols/msn/contact.c	Wed Jul 09 00:32:18 2008 +0000
@@ -28,7 +28,7 @@
 #include "contact.h"
 #include "xmlnode.h"
 #include "group.h"
-#include "soap2.h"
+#include "soap.h"
 #include "nexus.h"
 
 const char *MsnSoapPartnerScenarioText[] =
@@ -78,7 +78,7 @@
 	/* The rest should be made new */
 
 	return new_state;
-}	
+}
 
 void
 msn_callback_state_free(MsnCallbackState *state)
--- a/libpurple/protocols/msn/contact.h	Wed Jul 09 00:27:44 2008 +0000
+++ b/libpurple/protocols/msn/contact.h	Wed Jul 09 00:32:18 2008 +0000
@@ -27,6 +27,8 @@
 
 #include "session.h"
 
+#define MSN_APPLICATION_ID "CFE80F9D-180F-4399-82AB-413F33A1FA11"
+
 #define MSN_CONTACT_SERVER	"contacts.msn.com"
 
 /* Get Contact List */
@@ -43,7 +45,7 @@
 "<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">"\
 	"<soap:Header xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">"\
 		"<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
-			"<ApplicationId xmlns=\"http://www.msn.com/webservices/AddressBook\">09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId>"\
+			"<ApplicationId xmlns=\"http://www.msn.com/webservices/AddressBook\">" MSN_APPLICATION_ID "</ApplicationId>"\
 			"<IsMigration xmlns=\"http://www.msn.com/webservices/AddressBook\">false</IsMigration>"\
 			"<PartnerScenario xmlns=\"http://www.msn.com/webservices/AddressBook\">%s</PartnerScenario>"\
 		 "</ABApplicationHeader>"\
@@ -85,7 +87,7 @@
 	" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\
 	"<soap:Header>"\
 		"<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
-			"<ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId>"\
+			"<ApplicationId>" MSN_APPLICATION_ID "</ApplicationId>"\
 			"<IsMigration>false</IsMigration>"\
 			"<PartnerScenario>Initial</PartnerScenario>"\
 		"</ABApplicationHeader>"\
@@ -126,7 +128,7 @@
 	" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\
 	"<soap:Header>"\
 		"<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
-			"<ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId>"\
+			"<ApplicationId>" MSN_APPLICATION_ID "</ApplicationId>"\
 			"<IsMigration>false</IsMigration>"\
 			"<PartnerScenario>%s</PartnerScenario>"\
 		"</ABApplicationHeader>"\
@@ -155,7 +157,7 @@
 	" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\
 	"<soap:Header>"\
 		"<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
-			"<ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId>"\
+			"<ApplicationId>" MSN_APPLICATION_ID "</ApplicationId>"\
 			"<IsMigration>false</IsMigration>"\
 			"<PartnerScenario>Initial</PartnerScenario>"\
 		"</ABApplicationHeader>"\
@@ -215,7 +217,7 @@
 	" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\
 	"<soap:Header>"\
 		"<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
-			"<ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId>"\
+			"<ApplicationId>" MSN_APPLICATION_ID "</ApplicationId>"\
 			"<IsMigration>false</IsMigration>"\
 			"<PartnerScenario>ContactSave</PartnerScenario>"\
 		"</ABApplicationHeader>"\
@@ -244,7 +246,7 @@
 	" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\
 	"<soap:Header>"\
 		"<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
-			"<ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId>"\
+			"<ApplicationId>" MSN_APPLICATION_ID "</ApplicationId>"\
 			"<IsMigration>false</IsMigration>"\
 			"<PartnerScenario>ContactSave</PartnerScenario>"\
 		"</ABApplicationHeader>"\
@@ -281,7 +283,7 @@
 	" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\
 	"<soap:Header>"\
 		"<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
-			"<ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId>"\
+			"<ApplicationId>" MSN_APPLICATION_ID "</ApplicationId>"\
 			"<IsMigration>false</IsMigration>"\
 			"<PartnerScenario>Timer</PartnerScenario>"\
 		"</ABApplicationHeader>"\
@@ -308,7 +310,7 @@
 	" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\
 	"<soap:Header>"\
 		"<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
-			"<ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId>"\
+			"<ApplicationId>" MSN_APPLICATION_ID "</ApplicationId>"\
 			"<IsMigration>false</IsMigration>"\
 			"<PartnerScenario>Timer</PartnerScenario>"\
 		"</ABApplicationHeader>"\
@@ -341,7 +343,7 @@
 	" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\
 	"<soap:Header>"\
 		"<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
-			"<ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId>"\
+			"<ApplicationId>" MSN_APPLICATION_ID "</ApplicationId>"\
 			"<IsMigration>false</IsMigration>"\
 			"<PartnerScenario>Timer</PartnerScenario>"\
 		"</ABApplicationHeader>"\
@@ -396,7 +398,7 @@
 	" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\
 	"<soap:Header>"\
 		"<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
-			"<ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId>"\
+			"<ApplicationId>" MSN_APPLICATION_ID "</ApplicationId>"\
 			"<IsMigration>false</IsMigration>"\
 			"<PartnerScenario>%s</PartnerScenario>"\
 		"</ABApplicationHeader>"\
@@ -432,7 +434,7 @@
 	" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\
 	"<soap:Header>"\
 		"<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
-			"<ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId>"\
+			"<ApplicationId>" MSN_APPLICATION_ID "</ApplicationId>"\
 			"<IsMigration>false</IsMigration>"\
 			"<PartnerScenario>%s</PartnerScenario>"\
 		"</ABApplicationHeader>"\
@@ -476,7 +478,7 @@
 	" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\
 	"<soap:Header>"\
 		"<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
-			"<ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId>"\
+			"<ApplicationId>" MSN_APPLICATION_ID "</ApplicationId>"\
 			"<IsMigration>false</IsMigration>"\
 			"<PartnerScenario>GroupSave</PartnerScenario>"\
 		"</ABApplicationHeader>"\
@@ -518,7 +520,7 @@
 	" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\
 	"<soap:Header>"\
 		"<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
-			"<ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId>"\
+			"<ApplicationId>" MSN_APPLICATION_ID "</ApplicationId>"\
 			"<IsMigration>false</IsMigration>"\
 			"<PartnerScenario>Timer</PartnerScenario>"\
 		"</ABApplicationHeader>"\
@@ -549,7 +551,7 @@
 	" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\
 	"<soap:Header>"\
 		"<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
-			"<ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId>"\
+			"<ApplicationId>" MSN_APPLICATION_ID "</ApplicationId>"\
 			"<IsMigration>false</IsMigration>"\
 			"<PartnerScenario>Timer</PartnerScenario>"\
 		"</ABApplicationHeader>"\
@@ -670,7 +672,5 @@
 void msn_del_contact_from_list(MsnSession *session, MsnCallbackState *state,
 			       const gchar *passport, const MsnListId list);
 
-void msn_contact_connect_init(MsnSoapConn *soapconn);
-
 #endif /* _MSN_CONTACT_H_ */
 
--- a/libpurple/protocols/msn/group.h	Wed Jul 09 00:27:44 2008 +0000
+++ b/libpurple/protocols/msn/group.h	Wed Jul 09 00:32:18 2008 +0000
@@ -30,16 +30,8 @@
 
 #include "session.h"
 #include "user.h"
-#include "soap.h"
 #include "userlist.h"
 
-#define MSN_ADD_GROUPS	"<GroupInfo><name>test111</name><groupType>C8529CE2-6EAD-434d-881F-341E17DB3FF8</groupType><fMessenger>false</fMessenger><annotations><Annotation><Name>MSN.IM.Display</Name><Value>1</Value></Annotation></annotations></GroupInfo>"
-
-#define MSN_ADD_GROUP_TEMPLATE	"<?xml version=\"1.0\" encoding=\"utf-8\"?><soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\"><soap:Header><ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId><IsMigration>false</IsMigration><PartnerScenario>GroupSave</PartnerScenario></ABApplicationHeader><ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ManagedGroupRequest>false</ManagedGroupRequest></ABAuthHeader></soap:Header><soap:Body><ABGroupAdd xmlns=\"http://www.msn.com/webservices/AddressBook\"><abId>00000000-0000-0000-0000-000000000000</abId><groupAddOptions><fRenameOnMsgrConflict>false</fRenameOnMsgrConflict></groupAddOptions><groupInfo>%s</groupInfo></ABGroupAdd></soap:Body></soap:Envelope>"
-
-#define MSN_GROUP_IDS	"<guid>9e57e654-59f0-44d1-aedc-0a7500b7e51f</guid>"
-#define MSN_DELETE_GROUP_TEMPLATE	"<?xml version=\"1.0\" encoding=\"utf-8\"?><soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\"><soap:Header><ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId><IsMigration>false</IsMigration><PartnerScenario>Timer</PartnerScenario></ABApplicationHeader><ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ManagedGroupRequest>false</ManagedGroupRequest></ABAuthHeader></soap:Header><soap:Body><ABGroupDelete xmlns=\"http://www.msn.com/webservices/AddressBook\"><abId>00000000-0000-0000-0000-000000000000</abId><groupFilter><groupIds>%s</groupIds></groupFilter></ABGroupDelete></soap:Body></soap:Envelope>"
-
 #define MSN_INDIVIDUALS_GROUP_ID	"1983"
 #define MSN_INDIVIDUALS_GROUP_NAME	"Other Contacts"
 
@@ -52,7 +44,6 @@
 struct _MsnGroup
 {
 	MsnSession *session;    /**< The MSN session.           */
-	MsnSoapConn *soapconn;
 
 	char *id;                 /**< The group ID.              */
 	char *name;             /**< The name of the group.     */
--- a/libpurple/protocols/msn/httpconn.c	Wed Jul 09 00:27:44 2008 +0000
+++ b/libpurple/protocols/msn/httpconn.c	Wed Jul 09 00:32:18 2008 +0000
@@ -588,7 +588,8 @@
 
 	if (httpconn->virgin)
 	{
-		host = "gateway.messenger.hotmail.com";
+		/* QuLogic: This doesn't look right to me, but it still seems to work */
+		host = MSN_HTTPCONN_SERVER;
 
 		/* The first time servconn->host is the host we should connect to. */
 		params = g_strdup_printf("Action=open&Server=%s&IP=%s",
--- a/libpurple/protocols/msn/msn.c	Wed Jul 09 00:27:44 2008 +0000
+++ b/libpurple/protocols/msn/msn.c	Wed Jul 09 00:32:18 2008 +0000
@@ -2551,11 +2551,11 @@
 	PurpleAccountOption *option;
 
 	option = purple_account_option_string_new(_("Server"), "server",
-											WLM_SERVER);
+											MSN_SERVER);
 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,
 											   option);
 
-	option = purple_account_option_int_new(_("Port"), "port", WLM_PORT);
+	option = purple_account_option_int_new(_("Port"), "port", MSN_PORT);
 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,
 											   option);
 
--- a/libpurple/protocols/msn/msn.h	Wed Jul 09 00:27:44 2008 +0000
+++ b/libpurple/protocols/msn/msn.h	Wed Jul 09 00:32:18 2008 +0000
@@ -57,15 +57,10 @@
 
 #define MSN_BUF_LEN 8192
 
-#define USEROPT_MSNSERVER 3
+/* Windows Live Messenger Server*/
 #define MSN_SERVER "messenger.hotmail.com"
 #define MSN_HTTPCONN_SERVER "gateway.messenger.hotmail.com"
-#define USEROPT_MSNPORT 4
 #define MSN_PORT 1863
-
-/* Windows Live Messenger Server*/
-#define WLM_SERVER			"muser.messenger.hotmail.com"
-#define WLM_PORT			1863
 #define WLM_PROT_VER		15
 
 #define WLM_MAX_PROTOCOL	15
@@ -74,16 +69,13 @@
 #define MSN_TYPING_RECV_TIMEOUT 6
 #define MSN_TYPING_SEND_TIMEOUT	4
 
-#define HOTMAIL_URL "http://www.hotmail.com/cgi-bin/folders"w3
-#define PASSPORT_URL "http://lc1.law13.hotmail.passport.com/cgi-bin/dologin?login="
 #define PROFILE_URL "http://spaces.live.com/profile.aspx?mem="
 #define PHOTO_URL	" contactparams:photopreauthurl=\""
 
-#define USEROPT_HOTMAIL 0
-
 #define BUDDY_ALIAS_MAXLEN 387
 
-#define MSN_FT_GUID "{5D3E02AB-6190-11d3-BBBB-00C04F795683}"
+#define MSN_FT_GUID "5D3E02AB-6190-11D3-BBBB-00C04F795683"
+#define MSN_OBJ_GUID "A4268EEC-FEC5-49E5-95C3-F126696BDBF6"
 
 #define MSN_CLIENTINFO \
 	"Client-Name: Purple/" VERSION "\r\n" \
--- a/libpurple/protocols/msn/nexus.c	Wed Jul 09 00:27:44 2008 +0000
+++ b/libpurple/protocols/msn/nexus.c	Wed Jul 09 00:32:18 2008 +0000
@@ -22,7 +22,7 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
 #include "msn.h"
-#include "soap2.h"
+#include "soap.h"
 #include "nexus.h"
 #include "notification.h"
 
--- a/libpurple/protocols/msn/nexus.h	Wed Jul 09 00:27:44 2008 +0000
+++ b/libpurple/protocols/msn/nexus.h	Wed Jul 09 00:32:18 2008 +0000
@@ -24,8 +24,6 @@
 #ifndef _MSN_NEXUS_H_
 #define _MSN_NEXUS_H_
 
-#include "soap.h"
-
 /* Index into ticket_tokens in nexus.c Keep updated! */
 typedef enum
 {
--- a/libpurple/protocols/msn/notification.c	Wed Jul 09 00:27:44 2008 +0000
+++ b/libpurple/protocols/msn/notification.c	Wed Jul 09 00:32:18 2008 +0000
@@ -132,7 +132,7 @@
 	servconn = notification->servconn;
 
 	msn_servconn_set_connect_cb(servconn, connect_cb);
-	notification->in_use = msn_servconn_connect(servconn, host, port);
+	notification->in_use = msn_servconn_connect(servconn, host, port, TRUE);
 
 	return notification->in_use;
 }
@@ -328,7 +328,9 @@
 static void
 out_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
-	if (!g_ascii_strcasecmp(cmd->params[0], "OTH"))
+	if (cmd->param_count == 0)
+		msn_session_set_error(cmdproc->session, -1, NULL);
+	else if (!g_ascii_strcasecmp(cmd->params[0], "OTH"))
 		msn_session_set_error(cmdproc->session, MSN_ERROR_SIGN_OTHER,
 							  NULL);
 	else if (!g_ascii_strcasecmp(cmd->params[0], "SSD"))
@@ -360,7 +362,7 @@
 
 	msg = msn_message_new_from_cmd(cmdproc->session, cmd);
 
-	msn_message_parse_payload(msg, payload, len,MSG_LINE_DEM,MSG_BODY_DEM);
+	msn_message_parse_payload(msg, payload, len, MSG_LINE_DEM, MSG_BODY_DEM);
 #ifdef MSN_DEBUG_NS
 	msn_message_show_readable(msg, "Notification", TRUE);
 #endif
@@ -374,18 +376,16 @@
 msg_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
 	purple_debug_info("MSNP14","Processing MSG... \n");
-	if(cmd->payload_len == 0){
+	if (cmd->payload_len == 0) {
 		return;
 	}
 	/* NOTE: cmd is not always cmdproc->last_cmd, sometimes cmd is a queued
 	 * command and we are processing it */
-	if (cmd->payload == NULL)
-	{
+	if (cmd->payload == NULL) {
 		cmdproc->last_cmd->payload_cb  = msg_cmd_post;
 		cmdproc->servconn->payload_len = atoi(cmd->params[2]);
-	}
-	else
-	{
+
+	} else {
 		g_return_if_fail(cmd->payload_cb != NULL);
 
 #if 0 /* glib on win32 doesn't correctly support precision modifiers for a string */
@@ -418,6 +418,7 @@
 	msn_cmdproc_send_trans(cmdproc, trans);
 }
 
+#if 0
 static void
 ubm_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload,
 			 size_t len)
@@ -491,25 +492,26 @@
 	}
 	msn_message_destroy(msg);
 }
+#endif
 
 /*Yahoo msg process*/
 static void
 ubm_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
 	purple_debug_info("MSNP14","Processing UBM... \n");
-	if(cmd->payload_len == 0){
+	if (cmd->payload_len == 0) {
 		return;
 	}
 	/* NOTE: cmd is not always cmdproc->last_cmd, sometimes cmd is a queued
 	 * command and we are processing it */
-	if (cmd->payload == NULL){
-		cmdproc->last_cmd->payload_cb  = ubm_cmd_post;
-		cmdproc->servconn->payload_len = atoi(cmd->params[2]);
-	}else{
+	if (cmd->payload == NULL ){
+		cmdproc->last_cmd->payload_cb  = msg_cmd_post;
+		cmdproc->servconn->payload_len = atoi(cmd->params[4]);
+	} else {
 		g_return_if_fail(cmd->payload_cb != NULL);
 
 		purple_debug_info("MSNP14", "UBM payload:{%.*s}\n", (guint)(cmd->payload_len), cmd->payload);
-		ubm_cmd_post(cmdproc, cmd, cmd->payload, cmd->payload_len);
+		msg_cmd_post(cmdproc, cmd, cmd->payload, cmd->payload_len);
 	}
 }
 
--- a/libpurple/protocols/msn/oim.c	Wed Jul 09 00:27:44 2008 +0000
+++ b/libpurple/protocols/msn/oim.c	Wed Jul 09 00:32:18 2008 +0000
@@ -24,7 +24,7 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 #include "msn.h"
-#include "soap2.h"
+#include "soap.h"
 #include "oim.h"
 #include "msnutils.h"
 
--- a/libpurple/protocols/msn/oim.h	Wed Jul 09 00:27:44 2008 +0000
+++ b/libpurple/protocols/msn/oim.h	Wed Jul 09 00:32:18 2008 +0000
@@ -138,10 +138,8 @@
 {
 	MsnSession *session;
 
-	MsnSoapConn *retrieveconn;
 	GList * oim_list;
 
-	MsnSoapConn *sendconn;
 	char *challenge;
 	char *run_id;
 	gint send_seq;
--- a/libpurple/protocols/msn/servconn.c	Wed Jul 09 00:27:44 2008 +0000
+++ b/libpurple/protocols/msn/servconn.c	Wed Jul 09 00:32:18 2008 +0000
@@ -203,7 +203,7 @@
 }
 
 gboolean
-msn_servconn_connect(MsnServConn *servconn, const char *host, int port)
+msn_servconn_connect(MsnServConn *servconn, const char *host, int port, gboolean force)
 {
 	MsnSession *session;
 
@@ -223,7 +223,7 @@
 	{
 		/* HTTP Connection. */
 
-		if (!servconn->httpconn->connected)
+		if (!servconn->httpconn->connected || force)
 			if (!msn_httpconn_connect(servconn->httpconn, host, port))
 				return FALSE;
 
--- a/libpurple/protocols/msn/servconn.h	Wed Jul 09 00:27:44 2008 +0000
+++ b/libpurple/protocols/msn/servconn.h	Wed Jul 09 00:32:18 2008 +0000
@@ -115,8 +115,10 @@
  * @param servconn The connection.
  * @param host The host.
  * @param port The port.
+ * @param force Force this servconn to connect to a new server.
  */
-gboolean msn_servconn_connect(MsnServConn *servconn, const char *host, int port);
+gboolean msn_servconn_connect(MsnServConn *servconn, const char *host, int port,
+                              gboolean force);
 
 /**
  * Disconnects.
--- a/libpurple/protocols/msn/slp.c	Wed Jul 09 00:27:44 2008 +0000
+++ b/libpurple/protocols/msn/slp.c	Wed Jul 09 00:32:18 2008 +0000
@@ -251,7 +251,7 @@
 got_sessionreq(MsnSlpCall *slpcall, const char *branch,
 			   const char *euf_guid, const char *context)
 {
-	if (!strcmp(euf_guid, "A4268EEC-FEC5-49E5-95C3-F126696BDBF6"))
+	if (!strcmp(euf_guid, MSN_OBJ_GUID))
 	{
 		/* Emoticon or UserDisplay */
 		char *content;
@@ -332,7 +332,7 @@
 		msn_slplink_queue_slpmsg(slplink, slpmsg);
 		purple_imgstore_unref(img);
 	}
-	else if (!strcmp(euf_guid, "5D3E02AB-6190-11D3-BBBB-00C04F795683"))
+	else if (!strcmp(euf_guid, MSN_FT_GUID))
 	{
 		/* File Transfer */
 		PurpleAccount *account;
@@ -384,7 +384,8 @@
 
 			purple_xfer_request(xfer);
 		}
-	}
+	} else
+		purple_debug_warning("msn", "SLP SessionReq with unknown EUF-GUID: %s\n", euf_guid);
 }
 
 void
@@ -781,16 +782,13 @@
 got_emoticon(MsnSlpCall *slpcall,
 			 const guchar *data, gsize size)
 {
-
 	PurpleConversation *conv;
-	PurpleConnection *gc;
-	const char *who;
+	MsnSwitchBoard *swboard;
 
-	gc = slpcall->slplink->session->account->gc;
-	who = slpcall->slplink->remote_user;
+	swboard = slpcall->slplink->swboard;
+	conv = swboard->conv;
 
-	if ((conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY, who, gc->account))) {
-
+	if (conv) {
 		/* FIXME: it would be better if we wrote the data as we received it
 		   instead of all at once, calling write multiple times and
 		   close once at the very end
@@ -808,6 +806,7 @@
 {
 	MsnSession *session;
 	MsnSlpLink *slplink;
+	MsnSwitchBoard *swboard;
 	MsnObject *obj;
 	char **tokens;
 	char *smile, *body_str;
@@ -847,8 +846,9 @@
 
 		slplink = msn_session_get_slplink(session, who);
 
-		conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY, who,
-												   session->account);
+		swboard = cmdproc->data;
+		slplink->swboard = swboard;
+		conv = swboard->conv;
 
 		/* If the conversation doesn't exist then this is a custom smiley
 		 * used in the first message in a MSN conversation: we need to create
--- a/libpurple/protocols/msn/slplink.c	Wed Jul 09 00:27:44 2008 +0000
+++ b/libpurple/protocols/msn/slplink.c	Wed Jul 09 00:32:18 2008 +0000
@@ -593,7 +593,7 @@
 	}
 	else if (slpmsg->size)
 	{
-		if ((offset + len) > slpmsg->size)
+		if (G_MAXSIZE - len < offset || (offset + len) > slpmsg->size)
 		{
 			purple_debug_error("msn",
 				"Oversized slpmsg - msgsize=%lld offset=%" G_GSIZE_FORMAT " len=%" G_GSIZE_FORMAT "\n",
@@ -774,8 +774,7 @@
 
 	context = gen_context(fn, fp);
 
-	msn_slp_call_invite(slpcall, "5D3E02AB-6190-11D3-BBBB-00C04F795683", 2,
-						context);
+	msn_slp_call_invite(slpcall, MSN_FT_GUID, 2, context);
 
 	g_free(context);
 }
@@ -805,8 +804,7 @@
 	slpcall->cb = cb;
 	slpcall->end_cb = end_cb;
 
-	msn_slp_call_invite(slpcall, "A4268EEC-FEC5-49E5-95C3-F126696BDBF6", 1,
-						msnobj_base64);
+	msn_slp_call_invite(slpcall, MSN_OBJ_GUID, 1, msnobj_base64);
 
 	g_free(msnobj_base64);
 }
--- a/libpurple/protocols/msn/soap.c	Wed Jul 09 00:27:44 2008 +0000
+++ b/libpurple/protocols/msn/soap.c	Wed Jul 09 00:32:18 2008 +0000
@@ -1,8 +1,7 @@
 /**
  * @file soap.c
- * 	SOAP connection related process
- *	Author
- * 		MaYuan<mayuan2006@gmail.com>
+ * 	C file for SOAP connection related process
+ *
  * purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
@@ -23,854 +22,659 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
-#include "msn.h"
+
+#include "internal.h"
+
 #include "soap.h"
 
-#define MSN_SOAP_DEBUG
-/*local function prototype*/
-void msn_soap_set_process_step(MsnSoapConn *soapconn, MsnSoapStep step);
+#include "session.h"
+
+#include "debug.h"
+#include "xmlnode.h"
+
+#include <glib.h>
+#if !defined(_WIN32) || !defined(_WINERROR_)
+#include <error.h>
+#endif
+
+#define SOAP_TIMEOUT (5 * 60)
 
-/*setup the soap process step*/
-void
-msn_soap_set_process_step(MsnSoapConn *soapconn, MsnSoapStep step)
-{
-#ifdef MSN_SOAP_DEBUG
-	const char *MsnSoapStepText[] =
-	{
-		"Unconnected",
-		"Connecting",
-		"Connected",
-		"Processing",
-		"Connected Idle"
-	};
+typedef struct _MsnSoapRequest {
+	char *path;
+	MsnSoapMessage *message;
+	MsnSoapCallback cb;
+	gpointer cb_data;
+} MsnSoapRequest;
 
-	purple_debug_info("MSN SOAP", "Setting SOAP process step to %s\n", MsnSoapStepText[step]);
-#endif
-	soapconn->step = step;
-}
+typedef struct _MsnSoapConnection {
+	MsnSession *session;
+	char *host;
 
-/*new a soap connection*/
-MsnSoapConn *
-msn_soap_new(MsnSession *session,gpointer data, gboolean ssl)
-{
-	MsnSoapConn *soapconn;
-
-	soapconn = g_new0(MsnSoapConn, 1);
-	soapconn->session = session;
-	soapconn->parent = data;
-	soapconn->ssl_conn = ssl;
+	time_t last_used;
+	PurpleSslConnection *ssl;
+	gboolean connected;
 
-	soapconn->gsc = NULL;
-	soapconn->input_handler = 0;
-	soapconn->output_handler = 0;
-
-	msn_soap_set_process_step(soapconn, MSN_SOAP_UNCONNECTED);
-	soapconn->soap_queue = g_queue_new();
+	guint event_handle;
+	GString *buf;
+	gsize handled_len;
+	gsize body_len;
+	int response_code;
+	gboolean headers_done;
+	gboolean close_when_done;
 
-	return soapconn;
-}
+	MsnSoapMessage *message;
 
-/*ssl soap connect callback*/
-void
-msn_soap_connect_cb(gpointer data, PurpleSslConnection *gsc,
-				 PurpleInputCondition cond)
-{
-	MsnSoapConn * soapconn;
-	MsnSession *session;
-	gboolean soapconn_is_valid = FALSE;
+	GQueue *queue;
+	MsnSoapRequest *current_request;
+} MsnSoapConnection;
+
+static void msn_soap_connection_destroy_foreach_cb(gpointer item, gpointer data);
+static gboolean msn_soap_connection_run(gpointer data);
 
-	purple_debug_misc("MSN SOAP","SOAP server connection established!\n");
-
-	soapconn = data;
-	g_return_if_fail(soapconn != NULL);
+static MsnSoapConnection *msn_soap_connection_new(MsnSession *session,
+	const char *host);
+static void msn_soap_connection_handle_next(MsnSoapConnection *conn);
+static void msn_soap_connection_destroy(MsnSoapConnection *conn);
 
-	session = soapconn->session;
-	g_return_if_fail(session != NULL);
-
-	soapconn->gsc = gsc;
+static void msn_soap_message_send_internal(MsnSession *session,
+	MsnSoapMessage *message, const char *host, const char *path,
+	MsnSoapCallback cb, gpointer cb_data, gboolean first);
 
-	msn_soap_set_process_step(soapconn, MSN_SOAP_CONNECTED);
+static void msn_soap_request_destroy(MsnSoapRequest *req, gboolean keep_message);
+static void msn_soap_connection_sanitize(MsnSoapConnection *conn, gboolean disconnect);
+static gboolean msn_soap_write_cb_internal(gpointer data, gint fd, PurpleInputCondition cond, gboolean initial);
+static void msn_soap_process(MsnSoapConnection *conn);
 
-	/*connection callback*/
-	if (soapconn->connect_cb != NULL) {
-		soapconn_is_valid = soapconn->connect_cb(soapconn, gsc);
-	}
+static gboolean
+msn_soap_cleanup_each(gpointer key, gpointer value, gpointer data)
+{
+	MsnSoapConnection *conn = value;
+	time_t *t = data;
 
-	if (!soapconn_is_valid) {
-		return;
+	if ((*t - conn->last_used) > SOAP_TIMEOUT * 2) {
+		purple_debug_info("soap", "cleaning up soap conn %p\n", conn);
+		return TRUE;
 	}
 
-	/*we do the SOAP request here*/
-	msn_soap_post_head_request(soapconn);
-}
-
-/*ssl soap error callback*/
-static void
-msn_soap_error_cb(PurpleSslConnection *gsc, PurpleSslErrorType error, void *data)
-{
-	MsnSoapConn * soapconn = data;
-
-	g_return_if_fail(data != NULL);
-
-	purple_debug_warning("MSN SOAP","Soap connection error!\n");
-
-	msn_soap_set_process_step(soapconn, MSN_SOAP_UNCONNECTED);
-
-	/*error callback*/
-	if (soapconn->error_cb != NULL) {
-		soapconn->error_cb(soapconn, gsc, error);
-	} else {
-		msn_soap_post(soapconn, NULL);
-	}
-}
-
-/*init the soap connection*/
-void
-msn_soap_init(MsnSoapConn *soapconn,char * host, gboolean ssl,
-				MsnSoapSslConnectCbFunction connect_cb,
-				MsnSoapSslErrorCbFunction error_cb)
-{
-	purple_debug_misc("MSN SOAP","Initializing SOAP connection\n");
-	g_free(soapconn->login_host);
-	soapconn->login_host = g_strdup(host);
-	soapconn->ssl_conn = ssl;
-	soapconn->connect_cb = connect_cb;
-	soapconn->error_cb = error_cb;
+	return FALSE;
 }
 
-/*connect the soap connection*/
-void
-msn_soap_connect(MsnSoapConn *soapconn)
+static gboolean
+msn_soap_cleanup_for_session(gpointer data)
 {
-	if (soapconn->ssl_conn) {
-		purple_ssl_connect(soapconn->session->account, soapconn->login_host,
-				PURPLE_SSL_DEFAULT_PORT, msn_soap_connect_cb, msn_soap_error_cb,
-				soapconn);
-	} else {
+	MsnSession *sess = data;
+	time_t t = time(NULL);
+
+	purple_debug_info("soap", "session cleanup timeout\n");
+
+	if (sess->soap_table) {
+		g_hash_table_foreach_remove(sess->soap_table, msn_soap_cleanup_each,
+			&t);
+
+		if (g_hash_table_size(sess->soap_table) == 0) {
+			purple_timeout_remove(sess->soap_cleanup_handle);
+			sess->soap_cleanup_handle = 0;
+		}
 	}
 
-	msn_soap_set_process_step(soapconn, MSN_SOAP_CONNECTING);
-}
-
-
-static void
-msn_soap_close_handler(guint *handler)
-{
-	if (*handler > 0) {
-		purple_input_remove(*handler);
-		*handler = 0;
-	}
-#ifdef MSN_SOAP_DEBUG
-	else {
-		purple_debug_misc("MSN SOAP", "Handler inactive, not removing\n");
-	}
-#endif
-
-}
-
-
-/*close the soap connection*/
-void
-msn_soap_close(MsnSoapConn *soapconn)
-{
-	if (soapconn->ssl_conn) {
-		if (soapconn->gsc != NULL) {
-			purple_ssl_close(soapconn->gsc);
-			soapconn->gsc = NULL;
-		}
-	} else {
-	}
-	msn_soap_set_process_step(soapconn, MSN_SOAP_UNCONNECTED);
+	return TRUE;
 }
 
-/*clean the unhandled SOAP request*/
-void
-msn_soap_clean_unhandled_requests(MsnSoapConn *soapconn)
+static MsnSoapConnection *
+msn_soap_get_connection(MsnSession *session, const char *host)
 {
-	MsnSoapReq *request;
-
-	g_return_if_fail(soapconn != NULL);
-
-	soapconn->body = NULL;
-
-	while ((request = g_queue_pop_head(soapconn->soap_queue)) != NULL){
-		if (soapconn->read_cb) {
-			soapconn->read_cb(soapconn);
-		}
-		msn_soap_request_free(request);
-	}
-}
-
-/*destroy the soap connection*/
-void
-msn_soap_destroy(MsnSoapConn *soapconn)
-{
-	g_free(soapconn->login_host);
-
-	g_free(soapconn->login_path);
+	MsnSoapConnection *conn = NULL;
 
-	/*remove the write handler*/
-	if (soapconn->output_handler > 0){
-		purple_input_remove(soapconn->output_handler);
-		soapconn->output_handler = 0;
-	}
-	/*remove the read handler*/
-	if (soapconn->input_handler > 0){
-		purple_input_remove(soapconn->input_handler);
-		soapconn->input_handler = 0;
-	}
-	msn_soap_free_read_buf(soapconn);
-	msn_soap_free_write_buf(soapconn);
-
-	/*close ssl connection*/
-	msn_soap_close(soapconn);
-
-	/*process the unhandled soap request*/
-	msn_soap_clean_unhandled_requests(soapconn);
-
-	g_queue_free(soapconn->soap_queue);
-	g_free(soapconn);
-}
-
-/*check the soap is connected?
- * if connected return 1
- */
-int
-msn_soap_connected(MsnSoapConn *soapconn)
-{
-	if (soapconn->ssl_conn) {
-		return (soapconn->gsc == NULL ? 0 : 1);
-	}
-	return (soapconn->fd > 0 ? 1 : 0);
-}
-
-/*read and append the content to the buffer*/
-static gssize
-msn_soap_read(MsnSoapConn *soapconn)
-{
-	gssize len, requested_len;
-	char temp_buf[MSN_SOAP_READ_BUFF_SIZE];
-
-	if ( soapconn->need_to_read == 0 || soapconn->need_to_read > MSN_SOAP_READ_BUFF_SIZE) {
-		requested_len = MSN_SOAP_READ_BUFF_SIZE;
-	}
-	else {
-		requested_len = soapconn->need_to_read;
+	if (session->soap_table) {
+		conn = g_hash_table_lookup(session->soap_table, host);
+	} else {
+		session->soap_table = g_hash_table_new_full(g_str_hash, g_str_equal,
+			NULL, (GDestroyNotify)msn_soap_connection_destroy);
 	}
 
-	if ( soapconn->ssl_conn ) {
-		len = purple_ssl_read(soapconn->gsc, temp_buf, requested_len);
-	} else {
-		len = read(soapconn->fd, temp_buf, requested_len);
+	if (session->soap_cleanup_handle == 0)
+		session->soap_cleanup_handle = purple_timeout_add(SOAP_TIMEOUT * 1000,
+			msn_soap_cleanup_for_session, session);
+
+	if (conn == NULL) {
+		conn = msn_soap_connection_new(session, host);
+		g_hash_table_insert(session->soap_table, conn->host, conn);
 	}
 
+	conn->last_used = time(NULL);
 
-	if ( len <= 0 ) {
-		switch (errno) {
+	return conn;
+}
 
-			case 0:
-			case EBADF: /* we are sometimes getting this in Windows */
-			case EAGAIN: return len;
+static MsnSoapConnection *
+msn_soap_connection_new(MsnSession *session, const char *host)
+{
+	MsnSoapConnection *conn = g_new0(MsnSoapConnection, 1);
+	conn->session = session;
+	conn->host = g_strdup(host);
+	conn->queue = g_queue_new();
+	return conn;
+}
 
-			default : purple_debug_error("MSN SOAP", "Read error!"
-						"read len: %" G_GSSIZE_FORMAT ", error = %s\n",
-						len, g_strerror(errno));
-				  purple_input_remove(soapconn->input_handler);
-				  //soapconn->input_handler = 0;
-				  g_free(soapconn->read_buf);
-				  soapconn->read_buf = NULL;
-				  soapconn->read_len = 0;
-				  /* TODO: error handling */
-				  return len;
-		}
-	}
-	else {
-		soapconn->read_buf = g_realloc(soapconn->read_buf,
-						soapconn->read_len + len + 1);
-		if ( soapconn->read_buf != NULL ) {
-			memcpy(soapconn->read_buf + soapconn->read_len, temp_buf, len);
-			soapconn->read_len += len;
-			soapconn->read_buf[soapconn->read_len] = '\0';
-		}
-		else {
-			purple_debug_error("MSN SOAP",
-				"Failure re-allocating %" G_GSIZE_FORMAT " bytes of memory!\n",
-				soapconn->read_len + len + 1);
-			exit(EXIT_FAILURE);
-		}
+static void
+msn_soap_connected_cb(gpointer data, PurpleSslConnection *ssl,
+		PurpleInputCondition cond)
+{
+	MsnSoapConnection *conn = data;
+
+	conn->connected = TRUE;
 
-	}
+	if (conn->event_handle == 0)
+		conn->event_handle = purple_timeout_add(0, msn_soap_connection_run, conn);
+}
 
-#if defined(MSN_SOAP_DEBUG)
-	if (len > 0)
-		purple_debug_info("MSN SOAP",
-			"Read %" G_GSIZE_FORMAT " bytes from SOAP server:\n%s\n", len,
-			soapconn->read_buf + soapconn->read_len - len);
-#endif
+static void
+msn_soap_error_cb(PurpleSslConnection *ssl, PurpleSslErrorType error,
+		gpointer data)
+{
+	MsnSoapConnection *conn = data;
 
-	return len;
+	/* sslconn already frees the connection in case of error */
+	conn->ssl = NULL;
+
+	g_hash_table_remove(conn->session->soap_table, conn->host);
 }
 
-/*read the whole SOAP server response*/
-static void
-msn_soap_read_cb(gpointer data, gint source, PurpleInputCondition cond)
+static gboolean
+msn_soap_handle_redirect(MsnSoapConnection *conn, const char *url)
 {
-	MsnSoapConn *soapconn = data;
-	MsnSession *session;
-	int len;
-	char * body_start,*body_len;
-	char *length_start,*length_end;
-#ifdef MSN_SOAP_DEBUG
-#if !defined(_WIN32)
-	gchar * formattedxml = NULL;
-	gchar * http_headers = NULL;
-	xmlnode * node = NULL;
-#endif
-	purple_debug_misc("MSN SOAP", "msn_soap_read_cb()\n");
-#endif
-	session = soapconn->session;
-	g_return_if_fail(session != NULL);
+	char *host;
+	char *path;
+
+	if (purple_url_parse(url, &host, NULL, &path, NULL, NULL)) {
+		msn_soap_message_send_internal(conn->session,
+			conn->current_request->message,	host, path,
+			conn->current_request->cb, conn->current_request->cb_data, TRUE);
 
-
-	/*read the request header*/
-	len = msn_soap_read(soapconn);
+		msn_soap_request_destroy(conn->current_request, TRUE);
+		conn->current_request = NULL;
 
-	if ( len < 0 )
-		return;
+		g_free(host);
+		g_free(path);
 
-	if (soapconn->read_buf == NULL) {
-		return;
+		return TRUE;
 	}
 
-	if ( (strstr(soapconn->read_buf, "HTTP/1.1 302") != NULL)
-		|| ( strstr(soapconn->read_buf, "HTTP/1.1 301") != NULL ) )
-	{
-		/* Redirect. */
-		char *location, *c;
+	return FALSE;
+}
+
+static gboolean
+msn_soap_handle_body(MsnSoapConnection *conn, MsnSoapMessage *response)
+{
+	xmlnode *body = xmlnode_get_child(response->xml, "Body");
+	xmlnode *fault = xmlnode_get_child(response->xml, "Fault");
+
+	if (fault) {
+		xmlnode *faultcode = xmlnode_get_child(fault, "faultcode");
+
+		if (faultcode != NULL) {
+			char *faultdata = xmlnode_get_data(faultcode);
+
+			if (g_str_equal(faultdata, "psf:Redirect")) {
+				xmlnode *url = xmlnode_get_child(fault, "redirectUrl");
 
-		purple_debug_info("MSN SOAP", "HTTP Redirect\n");
-		location = strstr(soapconn->read_buf, "Location: ");
-		if (location == NULL)
-		{
-			c = (char *) g_strstr_len(soapconn->read_buf, soapconn->read_len,"\r\n\r\n");
-			if (c != NULL) {
-				/* we have read the whole HTTP headers and found no Location: */
-				msn_soap_free_read_buf(soapconn);
-				msn_soap_post(soapconn, NULL);
+				if (url) {
+					char *urldata = xmlnode_get_data(url);
+					msn_soap_handle_redirect(conn, urldata);
+					g_free(urldata);
+				}
+
+				g_free(faultdata);
+				msn_soap_message_destroy(response);
+				return TRUE;
+			} else if (g_str_equal(faultdata, "wsse:FailedAuthentication")) {
+				xmlnode *reason = xmlnode_get_child(fault, "faultstring");
+				char *reasondata = xmlnode_get_data(reason);
+
+				msn_soap_connection_sanitize(conn, TRUE);
+				msn_session_set_error(conn->session, MSN_ERROR_AUTH,
+					reasondata);
+
+				g_free(reasondata);
+				g_free(faultdata);
+				msn_soap_message_destroy(response);
+				return FALSE;
 			}
 
-			return;
-		}
-		location = strchr(location, ' ') + 1;
-
-		if ((c = strchr(location, '\r')) != NULL)
-			*c = '\0';
-		else
-			return;
-
-		/* Skip the http:// */
-		if ((c = strchr(location, '/')) != NULL)
-			location = c + 2;
-
-		if ((c = strchr(location, '/')) != NULL)
-		{
-			g_free(soapconn->login_path);
-			soapconn->login_path = g_strdup(c);
-
-			*c = '\0';
-		}
-
-		g_free(soapconn->login_host);
-		soapconn->login_host = g_strdup(location);
-
-		msn_soap_close_handler( &(soapconn->input_handler) );
-		msn_soap_close(soapconn);
-
-		if (purple_ssl_connect(session->account, soapconn->login_host,
-			PURPLE_SSL_DEFAULT_PORT, msn_soap_connect_cb,
-			msn_soap_error_cb, soapconn) == NULL) {
-
-			purple_debug_error("MSN SOAP", "Unable to connect to %s !\n", soapconn->login_host);
-			// dispatch next request
-			msn_soap_post(soapconn, NULL);
-		}
-	}
-	/* Another case of redirection, active on May, 2007
-	   See http://msnpiki.msnfanatic.com/index.php/MSNP13:SOAPTweener#Redirect
-	 */
-	else if (strstr(soapconn->read_buf,
-                    "<faultcode>psf:Redirect</faultcode>") != NULL)
-	{
-		char *location, *c;
-
-		if ( (location = strstr(soapconn->read_buf, "<psf:redirectUrl>") ) == NULL)
-			return;
-
-		/* Omit the tag preceding the URL */
-		location += strlen("<psf:redirectUrl>");
-		if (location > soapconn->read_buf + soapconn->read_len)
-			return;
-		if ( (location = strstr(location, "://")) == NULL)
-			return;
-
-		location += strlen("://"); /* Skip http:// or https:// */
-
-		if ( (c = strstr(location, "</psf:redirectUrl>")) != NULL )
-			*c = '\0';
-		else
-			return;
-
-		if ( (c = strstr(location, "/")) != NULL )
-		{
-			g_free(soapconn->login_path);
-			soapconn->login_path = g_strdup(c);
-			*c = '\0';
-		}
-
-		g_free(soapconn->login_host);
-		soapconn->login_host = g_strdup(location);
-
-		msn_soap_close_handler( &(soapconn->input_handler) );
-		msn_soap_close(soapconn);
-
-		if (purple_ssl_connect(session->account, soapconn->login_host,
-				PURPLE_SSL_DEFAULT_PORT, msn_soap_connect_cb,
-				msn_soap_error_cb, soapconn) == NULL) {
-
-			purple_debug_error("MSN SOAP", "Unable to connect to %s !\n", soapconn->login_host);
-			// dispatch next request
-			msn_soap_post(soapconn, NULL);
+			g_free(faultdata);
 		}
 	}
-	else if (strstr(soapconn->read_buf, "HTTP/1.1 401 Unauthorized") != NULL)
-	{
-		const char *error;
-
-		purple_debug_error("MSN SOAP", "Received HTTP error 401 Unauthorized\n");
-		if ((error = strstr(soapconn->read_buf, "WWW-Authenticate")) != NULL)
-		{
-			if ((error = strstr(error, "cbtxt=")) != NULL)
-			{
-				const char *c;
-				char *temp;
-
-				error += strlen("cbtxt=");
-
-				if ((c = strchr(error, '\n')) == NULL)
-					c = error + strlen(error);
-
-				temp = g_strndup(error, c - error);
-				error = purple_url_decode(temp);
-				g_free(temp);
-			}
-		}
-
-		msn_session_set_error(session, MSN_ERROR_AUTH, error);
-	}
-	/* Handle Passport 3.0 authentication failures.
-	 * Further info: http://msnpiki.msnfanatic.com/index.php/MSNP13:SOAPTweener
-	 */
-	else if (strstr(soapconn->read_buf,
-				"<faultcode>wsse:FailedAuthentication</faultcode>") != NULL)
-	{
-		gchar *faultstring;
-
-		faultstring = strstr(soapconn->read_buf, "<faultstring>");
-
-		if (faultstring != NULL)
-		{
-			gchar *c;
-			faultstring += strlen("<faultstring>");
-			if (faultstring < soapconn->read_buf + soapconn->read_len) {
-				c = strstr(soapconn->read_buf, "</faultstring>");
-				if (c != NULL) {
-					*c = '\0';
-					msn_session_set_error(session, MSN_ERROR_AUTH, faultstring);
-				}
-			}
-		}
-
-	}
-	else if (strstr(soapconn->read_buf, "HTTP/1.1 503 Service Unavailable"))
-	{
-		msn_session_set_error(session, MSN_ERROR_SERV_UNAVAILABLE, NULL);
-	}
-	else if ((strstr(soapconn->read_buf, "HTTP/1.1 200 OK"))
-		||(strstr(soapconn->read_buf, "HTTP/1.1 500")))
-	{
-		gboolean soapconn_is_valid = FALSE;
-
-		/*OK! process the SOAP body*/
-		body_start = (char *)g_strstr_len(soapconn->read_buf, soapconn->read_len,"\r\n\r\n");
-		if (!body_start) {
-			return;
-		}
-		body_start += 4;
-
-		if (body_start > soapconn->read_buf + soapconn->read_len)
-			return;
-
-		/* we read the content-length*/
-		if ( (length_start = g_strstr_len(soapconn->read_buf, soapconn->read_len, "Content-Length: ")) != NULL)
-			length_start += strlen("Content-Length: ");
-
-		if (length_start > soapconn->read_buf + soapconn->read_len)
-			return;
 
-		if ( (length_end = strstr(length_start, "\r\n")) == NULL )
-			return;
-
-		body_len = g_strndup(length_start, length_end - length_start);
-
-		/*setup the conn body */
-		soapconn->body		= body_start;
-		soapconn->body_len	= atoi(body_len);
-		g_free(body_len);
-#ifdef MSN_SOAP_DEBUG
-		purple_debug_misc("MSN SOAP",
-			"SOAP bytes read so far: %" G_GSIZE_FORMAT ", Content-Length: %d\n",
-			soapconn->read_len, soapconn->body_len);
-#endif
-		soapconn->need_to_read = (body_start - soapconn->read_buf + soapconn->body_len) - soapconn->read_len;
-		if ( soapconn->need_to_read > 0 ) {
-			return;
-		}
-
-#if defined(MSN_SOAP_DEBUG) && !defined(_WIN32)
-
-		node = xmlnode_from_str(soapconn->body, soapconn->body_len);
-
-		if (node != NULL) {
-			formattedxml = xmlnode_to_formatted_str(node, NULL);
-			http_headers = g_strndup(soapconn->read_buf, soapconn->body - soapconn->read_buf);
-
-			purple_debug_info("MSN SOAP","Data with XML payload received from the SOAP server:\n%s%s\n", http_headers, formattedxml);
-			g_free(http_headers);
-			g_free(formattedxml);
-			xmlnode_free(node);
-		}
-		else
-			purple_debug_info("MSN SOAP","Data received from the SOAP server:\n%s\n", soapconn->read_buf);
-#endif
+	if (fault || body) {
+		MsnSoapRequest *request = conn->current_request;
+		conn->current_request = NULL;
+		request->cb(request->message, response,
+			request->cb_data);
+		msn_soap_message_destroy(response);
+		msn_soap_request_destroy(request, FALSE);
+	}
 
-		/*remove the read handler*/
-		msn_soap_close_handler( &(soapconn->input_handler) );
-//		purple_input_remove(soapconn->input_handler);
-//		soapconn->input_handler = 0;
-		/*
-		 * close the soap connection,if more soap request came,
-		 * Just reconnect to do it,
-		 *
-		 * To solve the problem described below:
-		 * When I post the soap request in one socket one after the other,
-		 * The first read is ok, But the second soap read always got 0 bytes,
-		 * Weird!
-		 * */
-		msn_soap_close(soapconn);
-
-		/*call the read callback*/
-		if ( soapconn->read_cb != NULL ) {
-			soapconn_is_valid = soapconn->read_cb(soapconn);
-		}
-
-		if (!soapconn_is_valid) {
-			return;
-		}
-
-		/* dispatch next request in queue */
-		msn_soap_post(soapconn, NULL);
-	}
-	return;
-}
-
-void
-msn_soap_free_read_buf(MsnSoapConn *soapconn)
-{
-	g_return_if_fail(soapconn != NULL);
-
-	if (soapconn->read_buf) {
-		g_free(soapconn->read_buf);
-	}
-	soapconn->read_buf = NULL;
-	soapconn->read_len = 0;
-	soapconn->need_to_read = 0;
+	return TRUE;
 }
 
-void
-msn_soap_free_write_buf(MsnSoapConn *soapconn)
+static void
+msn_soap_read_cb(gpointer data, gint fd, PurpleInputCondition cond)
 {
-	g_return_if_fail(soapconn != NULL);
-
-	if (soapconn->write_buf) {
-		g_free(soapconn->write_buf);
-	}
-	soapconn->write_buf = NULL;
-	soapconn->written_len = 0;
-}
+	MsnSoapConnection *conn = data;
+	int count = 0, cnt, perrno;
+	/* This buffer needs to be larger than any packets received from
+		login.live.com or Adium will fail to receive the packet
+		(something weird with the login.live.com server). With NSS it works
+		fine, so I believe it's some bug with OS X */ 
+	char buf[16 * 1024];
 
-/*Soap write process func*/
-static void
-msn_soap_write_cb(gpointer data, gint source, PurpleInputCondition cond)
-{
-	MsnSoapConn *soapconn = data;
-	int len, total_len;
-
-	g_return_if_fail(soapconn != NULL);
-	if ( soapconn->write_buf == NULL ) {
-		purple_debug_error("MSN SOAP","SOAP write buffer is NULL\n");
-	//	msn_soap_check_conn_errors(soapconn);
-	//	purple_input_remove(soapconn->output_handler);
-	//	soapconn->output_handler = 0;
-		msn_soap_close_handler( &(soapconn->output_handler) );
-		return;
+	if (conn->message == NULL) {
+		conn->message = msn_soap_message_new(NULL, NULL);
 	}
-	total_len = strlen(soapconn->write_buf);
-
-	/*
-	 * write the content to SSL server,
-	 */
-	len = purple_ssl_write(soapconn->gsc,
-		soapconn->write_buf + soapconn->written_len,
-		total_len - soapconn->written_len);
-
-	if (len < 0 && errno == EAGAIN)
-		return;
-	else if (len <= 0){
-		/*SSL write error!*/
-//		msn_soap_check_conn_errors(soapconn);
 
-		msn_soap_close_handler( &(soapconn->output_handler) );
-//		purple_input_remove(soapconn->output_handler);
-//		soapconn->output_handler = 0;
-
-		msn_soap_close(soapconn);
+	if (conn->buf == NULL) {
+		conn->buf = g_string_new_len(buf, 0);
+	}
+	
+	while ((cnt = purple_ssl_read(conn->ssl, buf, sizeof(buf))) > 0) {
+		purple_debug_info("soap", "read %d bytes\n", cnt);
+		count += cnt;
+		g_string_append_len(conn->buf, buf, cnt);
+	}
 
-		/* TODO: notify of the error */
-		purple_debug_error("MSN SOAP", "Error writing to SSL connection!\n");
-		msn_soap_post(soapconn, NULL);
-		return;
-	}
-	soapconn->written_len += len;
-
-	if (soapconn->written_len < total_len)
+	/* && count is necessary for Adium, on OS X the last read always
+	   return an error, so we want to proceed anyway. See #5212 for
+	   discussion on this and the above buffer size issues */
+	if(cnt < 0 && errno == EAGAIN && count == 0)
 		return;
 
-	msn_soap_close_handler( &(soapconn->output_handler) );
-//	purple_input_remove(soapconn->output_handler);
-//	soapconn->output_handler = 0;
-
-	/*clear the write buff*/
-	msn_soap_free_write_buf(soapconn);
-
-	/* Write finish!
-	 * callback for write done
-	 */
-	if(soapconn->written_cb != NULL){
-		soapconn->written_cb(soapconn);
-	}
-	/*maybe we need to read the input?*/
-	if ( soapconn->input_handler == 0 ) {
-		soapconn->input_handler = purple_input_add(soapconn->gsc->fd,
-			PURPLE_INPUT_READ, msn_soap_read_cb, soapconn);
-	}
-}
-
-/*write the buffer to SOAP connection*/
-void
-msn_soap_write(MsnSoapConn * soapconn, char *write_buf, MsnSoapWrittenCbFunction written_cb)
-{
-	if (soapconn == NULL) {
-		return;
-	}
-
-	msn_soap_set_process_step(soapconn, MSN_SOAP_PROCESSING);
-
-	/* Ideally this wouldn't ever be necessary, but i believe that it is leaking the previous value */
-	g_free(soapconn->write_buf);
-	soapconn->write_buf = write_buf;
-	soapconn->written_len = 0;
-	soapconn->written_cb = written_cb;
-
-	msn_soap_free_read_buf(soapconn);
-
-	/*clear the read buffer first*/
-	/*start the write*/
-	soapconn->output_handler = purple_input_add(soapconn->gsc->fd, PURPLE_INPUT_WRITE,
-						    msn_soap_write_cb, soapconn);
-	msn_soap_write_cb(soapconn, soapconn->gsc->fd, PURPLE_INPUT_WRITE);
-}
-
-/* New a soap request*/
-MsnSoapReq *
-msn_soap_request_new(const char *host,const char *post_url,const char *soap_action,
-				const char *body, const gpointer data_cb,
-				MsnSoapReadCbFunction read_cb,
-				MsnSoapWrittenCbFunction written_cb,
-				MsnSoapConnectInitFunction connect_init)
-{
-	MsnSoapReq *request;
-
-	request = g_new0(MsnSoapReq, 1);
-	request->id = 0;
-
-	request->login_host = g_strdup(host);
-	request->login_path = g_strdup(post_url);
-	request->soap_action		= g_strdup(soap_action);
-	request->body		= g_strdup(body);
-	request->data_cb 	= data_cb;
-	request->read_cb	= read_cb;
-	request->written_cb	= written_cb;
-	request->connect_init	= connect_init;
-
-	return request;
-}
-
-/*free a soap request*/
-void
-msn_soap_request_free(MsnSoapReq *request)
-{
-	g_return_if_fail(request != NULL);
-
-	g_free(request->login_host);
-	g_free(request->login_path);
-	g_free(request->soap_action);
-	g_free(request->body);
-	request->read_cb	= NULL;
-	request->written_cb	= NULL;
-	request->connect_init	= NULL;
-
-	g_free(request);
-}
-
-/*post the soap request queue's head request*/
-void
-msn_soap_post_head_request(MsnSoapConn *soapconn)
-{
-	g_return_if_fail(soapconn != NULL);
-	g_return_if_fail(soapconn->soap_queue != NULL);
-
-	if (soapconn->step == MSN_SOAP_CONNECTED ||
-	    soapconn->step == MSN_SOAP_CONNECTED_IDLE) {
-
-		purple_debug_info("MSN SOAP", "Posting new request from head of the queue\n");
-
-		if ( !g_queue_is_empty(soapconn->soap_queue) ) {
-			MsnSoapReq *request;
-
-			if ( (request = g_queue_pop_head(soapconn->soap_queue)) != NULL ) {
-				msn_soap_post_request(soapconn,request);
-			}
-		} else {
-			purple_debug_info("MSN SOAP", "No requests to process found.\n");
-			msn_soap_set_process_step(soapconn, MSN_SOAP_CONNECTED_IDLE);
+	// msn_soap_process could alter errno
+	perrno = errno;
+	msn_soap_process(conn);
+	
+	if (cnt < 0 && perrno != EAGAIN) {
+		purple_debug_info("soap", "read: %s\n", g_strerror(perrno));
+		// It's possible msn_soap_process closed the ssl connection
+		if (conn->ssl) {
+			purple_ssl_close(conn->ssl);
+			conn->ssl = NULL;
+			msn_soap_connection_handle_next(conn);
 		}
 	}
 }
 
-/*post the soap request ,
- * if not connected, Connected first.
- */
-void
-msn_soap_post(MsnSoapConn *soapconn, MsnSoapReq *request)
-{
-	MsnSoapReq *head_request;
+static void
+msn_soap_process(MsnSoapConnection *conn) {
+	gboolean handled = FALSE;
+	char *cursor;
+	char *linebreak;
+
+	purple_debug_info("soap", "current %s\n", conn->buf->str);
+
+	cursor = conn->buf->str + conn->handled_len;
+
+	if (!conn->headers_done) {
+		while ((linebreak = strstr(cursor, "\r\n"))	!= NULL) {
+			conn->handled_len = linebreak - conn->buf->str + 2;
+
+			if (conn->response_code == 0) {
+				if (sscanf(cursor, "HTTP/1.1 %d", &conn->response_code) != 1) {
+					/* something horribly wrong */
+					purple_ssl_close(conn->ssl);
+					conn->ssl = NULL;
+					msn_soap_connection_handle_next(conn);
+					handled = TRUE;
+					break;
+				} else if (conn->response_code == 503) {
+					msn_soap_connection_sanitize(conn, TRUE);
+					msn_session_set_error(conn->session, MSN_ERROR_SERV_UNAVAILABLE, NULL);
+					return;
+				}
+			} else if (cursor == linebreak) {
+				/* blank line */
+				conn->headers_done = TRUE;
+				cursor = conn->buf->str + conn->handled_len;
+				break;
+			} else {
+				char *line = g_strndup(cursor, linebreak - cursor);
+				char *sep = strstr(line, ": ");
+				char *key = line;
+				char *value;
 
-	if (soapconn == NULL)
-		return;
+				if (sep == NULL) {
+					purple_debug_info("soap", "ignoring malformed line: %s\n", line);
+					g_free(line);
+					goto loop_end;
+				}
+
+				value = sep + 2;
+				*sep = '\0';
+				msn_soap_message_add_header(conn->message, key, value);
+
+				if ((conn->response_code == 301 || conn->response_code == 300)
+					&& strcmp(key, "Location") == 0) {
+
+					msn_soap_handle_redirect(conn, value);
+
+					handled = TRUE;
+					g_free(line);
+					break;
+				} else if (conn->response_code == 401 &&
+					strcmp(key, "WWW-Authenticate") == 0) {
+					char *error = strstr(value, "cbtxt=");
 
-	if (request != NULL) {
-#ifdef MSN_SOAP_DEBUG
-		purple_debug_misc("MSN SOAP", "Request added to the queue\n");
-#endif
-		g_queue_push_tail(soapconn->soap_queue, request);
+					if (error) {
+						error += strlen("cbtxt=");
+					}
+
+					msn_soap_connection_sanitize(conn, TRUE);
+					msn_session_set_error(conn->session, MSN_ERROR_AUTH,
+						error ? purple_url_decode(error) : NULL);
+
+					g_free(line);
+					return;
+				} else if (strcmp(key, "Content-Length") == 0) {
+					conn->body_len = atoi(value);
+				} else if (strcmp(key, "Connection") == 0) {
+					if (strcmp(value, "close") == 0) {
+						conn->close_when_done = TRUE;
+					}
+				}
+				g_free(line);
+			}
+
+		loop_end:
+			cursor = conn->buf->str + conn->handled_len;
+		}
 	}
 
-	if ( !g_queue_is_empty(soapconn->soap_queue)) {
-
-		/* we may have to reinitialize the soap connection, so avoid
-		 * reusing the connection for now */
+	if (!handled && conn->headers_done) {
+		if (conn->buf->len - conn->handled_len >=
+			conn->body_len) {
+			xmlnode *node = xmlnode_from_str(cursor, conn->body_len);
 
-		if (soapconn->step == MSN_SOAP_CONNECTED_IDLE) {
-			purple_debug_misc("MSN SOAP","Already connected to SOAP server, re-initializing\n");
-			msn_soap_close_handler( &(soapconn->input_handler) );
-			msn_soap_close_handler( &(soapconn->output_handler) );
-			msn_soap_close(soapconn);
-		}
+			if (node == NULL) {
+				purple_debug_info("soap", "Malformed SOAP response: %s\n",
+					cursor);
+			} else {
+				MsnSoapMessage *message = conn->message;
+				conn->message = NULL;
+				message->xml = node;
 
-		if (!msn_soap_connected(soapconn) && (soapconn->step == MSN_SOAP_UNCONNECTED)) {
-
-			/*not connected?and we have something to process connect it first*/
-			purple_debug_misc("MSN SOAP","No connection to SOAP server. Connecting...\n");
-			head_request = g_queue_peek_head(soapconn->soap_queue);
-
-			if (head_request == NULL) {
-				purple_debug_error("MSN SOAP", "Queue is not empty, but failed to peek the head request!\n");
-				return;
+				if (!msn_soap_handle_body(conn, message)) {
+					return;
+				}
 			}
 
-			if (head_request->connect_init != NULL) {
-				head_request->connect_init(soapconn);
-			}
-			msn_soap_connect(soapconn);
-			return;
+			msn_soap_connection_handle_next(conn);
 		}
 
-#ifdef MSN_SOAP_DEBUG
-		purple_debug_info("MSN SOAP", "Currently processing another SOAP request\n");
-	} else {
-		purple_debug_info("MSN SOAP", "No requests left to dispatch\n");
-#endif
+		return;
+	}
+
+	if (handled) {
+		msn_soap_connection_handle_next(conn);
+	}
+}
+
+static void
+msn_soap_write_cb(gpointer data, gint fd, PurpleInputCondition cond)
+{
+	msn_soap_write_cb_internal(data, fd, cond, FALSE);
+}
+
+static gboolean
+msn_soap_write_cb_internal(gpointer data, gint fd, PurpleInputCondition cond,
+		gboolean initial)
+{
+	MsnSoapConnection *conn = data;
+	int written;
+
+	if (cond != PURPLE_INPUT_WRITE) return TRUE;
+
+	written = purple_ssl_write(conn->ssl, conn->buf->str + conn->handled_len,
+		conn->buf->len - conn->handled_len);
+
+	if (written < 0 && errno == EAGAIN)
+		return TRUE;
+	else if (written <= 0) {
+		purple_ssl_close(conn->ssl);
+		conn->ssl = NULL;
+		if (!initial) msn_soap_connection_handle_next(conn);
+		return FALSE;
 	}
 
+	conn->handled_len += written;
+
+	if (conn->handled_len < conn->buf->len)
+		return TRUE;
+
+	/* we are done! */
+	g_string_free(conn->buf, TRUE);
+	conn->buf = NULL;
+	conn->handled_len = 0;
+	conn->body_len = 0;
+	conn->response_code = 0;
+	conn->headers_done = FALSE;
+	conn->close_when_done = FALSE;
+
+	purple_input_remove(conn->event_handle);
+	conn->event_handle = purple_input_add(conn->ssl->fd, PURPLE_INPUT_READ,
+		msn_soap_read_cb, conn);
+	return TRUE;
+}
+
+static gboolean
+msn_soap_connection_run(gpointer data)
+{
+	MsnSoapConnection *conn = data;
+	MsnSoapRequest *req = g_queue_peek_head(conn->queue);
+
+	conn->event_handle = 0;
+
+	if (req) {
+		if (conn->ssl == NULL) {
+			conn->ssl = purple_ssl_connect(conn->session->account, conn->host,
+				443, msn_soap_connected_cb, msn_soap_error_cb, conn);
+		} else if (conn->connected) {
+			int len = -1;
+			char *body = xmlnode_to_str(req->message->xml, &len);
+			GSList *iter;
+
+			g_queue_pop_head(conn->queue);
+
+			conn->buf = g_string_new("");
+
+			g_string_append_printf(conn->buf,
+				"POST /%s HTTP/1.1\r\n"
+				"SOAPAction: %s\r\n"
+				"Content-Type:text/xml; charset=utf-8\r\n"
+				"User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)\r\n"
+				"Accept: */*\r\n"
+				"Host: %s\r\n"
+				"Content-Length: %d\r\n"
+				"Connection: Keep-Alive\r\n"
+				"Cache-Control: no-cache\r\n",
+				req->path, req->message->action ? req->message->action : "",
+				conn->host, len);
+
+			for (iter = req->message->headers; iter; iter = iter->next) {
+				g_string_append(conn->buf, (char *)iter->data);
+				g_string_append(conn->buf, "\r\n");
+			}
+
+			g_string_append(conn->buf, "\r\n");
+			g_string_append(conn->buf, body);
+
+			purple_debug_info("soap", "%s\n", conn->buf->str);
+
+			conn->handled_len = 0;
+			conn->current_request = req;
+
+			conn->event_handle = purple_input_add(conn->ssl->fd,
+				PURPLE_INPUT_WRITE, msn_soap_write_cb, conn);
+			if (!msn_soap_write_cb_internal(conn, conn->ssl->fd, PURPLE_INPUT_WRITE, TRUE)) {
+				/* Not connected => reconnect and retry */
+				purple_debug_info("soap", "not connected, reconnecting\n");
+				
+				conn->connected = FALSE;
+				conn->current_request = NULL;
+				msn_soap_connection_sanitize(conn, FALSE);
+				
+				g_queue_push_head(conn->queue, req);
+				conn->event_handle = purple_timeout_add(0, msn_soap_connection_run, conn);
+			}
+
+			g_free(body);
+		}
+	}
+
+	return FALSE;
+}
+
+void
+msn_soap_message_send(MsnSession *session, MsnSoapMessage *message,
+	const char *host, const char *path,
+	MsnSoapCallback cb, gpointer cb_data)
+{
+	msn_soap_message_send_internal(session, message, host, path, cb, cb_data,
+		FALSE);
 }
 
-/*Post the soap request action*/
-void
-msn_soap_post_request(MsnSoapConn *soapconn, MsnSoapReq *request)
+static void
+msn_soap_message_send_internal(MsnSession *session,
+	MsnSoapMessage *message, const char *host, const char *path,
+	MsnSoapCallback cb, gpointer cb_data, gboolean first)
 {
-	char * request_str = NULL;
-#ifdef MSN_SOAP_DEBUG
-#if !defined(_WIN32)
-	xmlnode * node;
-#endif
-	purple_debug_misc("MSN SOAP","msn_soap_post_request()\n");
-#endif
+	MsnSoapConnection *conn = msn_soap_get_connection(session, host);
+	MsnSoapRequest *req = g_new0(MsnSoapRequest, 1);
+
+	req->path = g_strdup(path);
+	req->message = message;
+	req->cb = cb;
+	req->cb_data = cb_data;
+
+	if (first) {
+		g_queue_push_head(conn->queue, req);
+	} else {
+		g_queue_push_tail(conn->queue, req);
+	}
+
+	if (conn->event_handle == 0)
+		conn->event_handle = purple_timeout_add(0, msn_soap_connection_run,
+			conn);
+}
+
+static void
+msn_soap_connection_sanitize(MsnSoapConnection *conn, gboolean disconnect)
+{
+	if (conn->event_handle) {
+		purple_input_remove(conn->event_handle);
+		conn->event_handle = 0;
+	}
 
-	msn_soap_set_process_step(soapconn, MSN_SOAP_PROCESSING);
-	request_str = g_strdup_printf(
-					"POST %s HTTP/1.1\r\n"
-					"SOAPAction: %s\r\n"
-					"Content-Type:text/xml; charset=utf-8\r\n"
-					"User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)\r\n"
-					"Accept: */*\r\n"
-					"Host: %s\r\n"
-					"Content-Length: %" G_GSIZE_FORMAT "\r\n"
-					"Connection: Keep-Alive\r\n"
-					"Cache-Control: no-cache\r\n\r\n"
-					"%s",
-					request->login_path,
-					request->soap_action,
-					request->login_host,
-					strlen(request->body),
-					request->body
-					);
+	if (conn->message) {
+		msn_soap_message_destroy(conn->message);
+		conn->message = NULL;
+	}
+
+	if (conn->buf) {
+		g_string_free(conn->buf, TRUE);
+		conn->buf = NULL;
+	}
+
+	if (conn->ssl && (disconnect || conn->close_when_done)) {
+		purple_ssl_close(conn->ssl);
+		conn->ssl = NULL;
+	}
 
-#if defined(MSN_SOAP_DEBUG) && !defined(_WIN32)
-	node = xmlnode_from_str(request->body, -1);
-	if (node != NULL) {
-		char *formattedstr = xmlnode_to_formatted_str(node, NULL);
-		purple_debug_info("MSN SOAP","Posting request to SOAP server:\n%s%s\n",request_str, formattedstr);
-		g_free(formattedstr);
-		xmlnode_free(node);
+	if (conn->current_request) {
+		msn_soap_request_destroy(conn->current_request, FALSE);
+		conn->current_request = NULL;
 	}
-	else
-		purple_debug_info("MSN SOAP","Failed to parse SOAP request being sent:\n%s\n", request_str);
-#endif
+}
+
+static void
+msn_soap_connection_handle_next(MsnSoapConnection *conn)
+{
+	msn_soap_connection_sanitize(conn, FALSE);
 
-	/*free read buffer*/
-	// msn_soap_free_read_buf(soapconn);
-	/*post it to server*/
-	soapconn->data_cb = request->data_cb;
-	msn_soap_write(soapconn, request_str, request->written_cb);
+	conn->event_handle = purple_timeout_add(0, msn_soap_connection_run,	conn);
+
+	if (conn->current_request) {
+		MsnSoapRequest *req = conn->current_request;
+		conn->current_request = NULL;
+		msn_soap_connection_destroy_foreach_cb(req, conn);
+	}
 }
 
+static void
+msn_soap_connection_destroy_foreach_cb(gpointer item, gpointer data)
+{
+	MsnSoapRequest *req = item;
+
+	if (req->cb)
+		req->cb(req->message, NULL, req->cb_data);
+
+	msn_soap_request_destroy(req, FALSE);
+}
+
+static void
+msn_soap_connection_destroy(MsnSoapConnection *conn)
+{
+	if (conn->current_request) {
+		MsnSoapRequest *req = conn->current_request;
+		conn->current_request = NULL;
+		msn_soap_connection_destroy_foreach_cb(req, conn);
+	}
+
+	msn_soap_connection_sanitize(conn, TRUE);
+	g_queue_foreach(conn->queue, msn_soap_connection_destroy_foreach_cb, conn);
+	g_queue_free(conn->queue);
+
+	g_free(conn->host);
+	g_free(conn);
+}
+
+MsnSoapMessage *
+msn_soap_message_new(const char *action, xmlnode *xml)
+{
+	MsnSoapMessage *message = g_new0(MsnSoapMessage, 1);
+
+	message->action = g_strdup(action);
+	message->xml = xml;
+
+	return message;
+}
+
+void
+msn_soap_message_destroy(MsnSoapMessage *message)
+{
+	if (message) {
+		g_slist_foreach(message->headers, (GFunc)g_free, NULL);
+		g_slist_free(message->headers);
+		g_free(message->action);
+		if (message->xml)
+			xmlnode_free(message->xml);
+		g_free(message);
+	}
+}
+
+void
+msn_soap_message_add_header(MsnSoapMessage *message,
+		const char *name, const char *value)
+{
+	char *header = g_strdup_printf("%s: %s\r\n", name, value);
+
+	message->headers = g_slist_prepend(message->headers, header);
+}
+
+static void
+msn_soap_request_destroy(MsnSoapRequest *req, gboolean keep_message)
+{
+	g_free(req->path);
+	if (!keep_message)
+		msn_soap_message_destroy(req->message);
+	g_free(req);
+}
+
--- a/libpurple/protocols/msn/soap.h	Wed Jul 09 00:27:44 2008 +0000
+++ b/libpurple/protocols/msn/soap.h	Wed Jul 09 00:32:18 2008 +0000
@@ -1,8 +1,7 @@
 /**
  * @file soap.h
  * 	header file for SOAP connection related process
- *	Author
- * 		MaYuan<mayuan2006@gmail.com>
+ *
  * purple
  *
  * Purple is the legal property of its developers, whose names are too numerous
@@ -23,144 +22,35 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
-#ifndef _MSN_SOAP_H_
-#define _MSN_SOAP_H_
 
-#define MSN_SOAP_READ_BUFF_SIZE		8192
-
-/* define this to debug the communications with the SOAP server */
-/* #define MSN_SOAP_DEBUG */
-
-#define MSN_SOAP_READ 1
-#define MSN_SOAP_WRITE 2
+#ifndef _MSN_SOAP_H
+#define _MSN_SOAP_H
 
-typedef enum
-{
-	MSN_SOAP_UNCONNECTED,
-	MSN_SOAP_CONNECTING,
-	MSN_SOAP_CONNECTED,
-	MSN_SOAP_PROCESSING,
-	MSN_SOAP_CONNECTED_IDLE
-}MsnSoapStep;
-
-/* MSN SoapRequest structure*/
-typedef struct _MsnSoapReq MsnSoapReq;
+#include "session.h"
+#include "sslconn.h"
+#include "xmlnode.h"
 
-/* MSN Https connection structure*/
-typedef struct _MsnSoapConn MsnSoapConn;
-
-typedef void (*MsnSoapConnectInitFunction)(MsnSoapConn *);
-typedef gboolean (*MsnSoapReadCbFunction)(MsnSoapConn *);
-typedef void (*MsnSoapWrittenCbFunction)(MsnSoapConn *);
-
-typedef gboolean (*MsnSoapSslConnectCbFunction)(MsnSoapConn *, PurpleSslConnection *);
-typedef void (*MsnSoapSslErrorCbFunction)(MsnSoapConn *, PurpleSslConnection *, PurpleSslErrorType);
-
+#include <glib.h>
 
-struct _MsnSoapReq{
-	/*request sequence*/
-	int	 id;
+typedef struct _MsnSoapMessage MsnSoapMessage;
+typedef void (*MsnSoapCallback)(MsnSoapMessage *request,
+	MsnSoapMessage *response, gpointer cb_data);
 
-	char *login_host;
-	char *login_path;
-	char *soap_action;
-
-	char *body;
-
-	gpointer data_cb;
-	MsnSoapReadCbFunction read_cb;
-	MsnSoapWrittenCbFunction written_cb;
-	MsnSoapConnectInitFunction connect_init;
+struct _MsnSoapMessage {
+	char *action;
+	xmlnode *xml;
+	GSList *headers;
 };
 
-struct _MsnSoapConn{
-	MsnSession *session;
-	gpointer parent;
-
-	char *login_host;
-	char *login_path;
-	char *soap_action;
-
-	MsnSoapStep step;
-	/*ssl connection?*/
-	gboolean ssl_conn;
-	/*normal connection*/
-	guint fd;
-	/*SSL connection*/
-	PurpleSslConnection *gsc;
-	/*ssl connection callback*/
-	MsnSoapSslConnectCbFunction connect_cb;
-	/*ssl error callback*/
-	MsnSoapSslErrorCbFunction error_cb;
+MsnSoapMessage *msn_soap_message_new(const char *action, xmlnode *xml);
 
-	/*read handler*/
-	guint input_handler;
-	/*write handler*/
-	guint output_handler;
-
-	/*Queue of SOAP request to send*/
-	int soap_id;
-	GQueue *soap_queue;
-
-	/*write buffer*/
-	char *write_buf;
-	gsize written_len;
-	MsnSoapWrittenCbFunction written_cb;
-
-	/*read buffer*/
-	char *read_buf;
-	gsize read_len;
-	gsize need_to_read;
-	MsnSoapReadCbFunction read_cb;
-
-	gpointer data_cb;
+void msn_soap_message_add_header(MsnSoapMessage *req,
+	const char *name, const char *value);
 
-	/*HTTP reply body part*/
-	char *body;
-	int body_len;
-};
-
-
-/*Function Prototype*/
-/*Soap Request Function */
-MsnSoapReq *msn_soap_request_new(const char *host, const char *post_url,
-				 const char *soap_action, const char *body,
-				 const gpointer data_cb,
-				 MsnSoapReadCbFunction read_cb,
-				 MsnSoapWrittenCbFunction written_cb,
-				 MsnSoapConnectInitFunction connect_init);
-
-void msn_soap_request_free(MsnSoapReq *request);
-void msn_soap_post_request(MsnSoapConn *soapconn,MsnSoapReq *request);
-void msn_soap_post_head_request(MsnSoapConn *soapconn);
-
-/*new a soap conneciton */
-MsnSoapConn *msn_soap_new(MsnSession *session, gpointer data, gboolean ssl);
-
-/*destroy */
-void msn_soap_destroy(MsnSoapConn *soapconn);
+void msn_soap_message_send(MsnSession *session,
+	MsnSoapMessage *message, const char *host, const char *path,
+	MsnSoapCallback cb, gpointer cb_data);
 
-/*init a soap conneciton */
-void msn_soap_init(MsnSoapConn *soapconn, char * host, gboolean ssl,
-		   MsnSoapSslConnectCbFunction connect_cb,
-		   MsnSoapSslErrorCbFunction error_cb);
-void msn_soap_connect(MsnSoapConn *soapconn);
-void msn_soap_close(MsnSoapConn *soapconn);
-
-/*write to soap*/
-void msn_soap_write(MsnSoapConn * soapconn, char *write_buf, MsnSoapWrittenCbFunction written_cb);
-void msn_soap_post(MsnSoapConn *soapconn,MsnSoapReq *request);
+void msn_soap_message_destroy(MsnSoapMessage *message);
 
-void msn_soap_free_read_buf(MsnSoapConn *soapconn);
-void msn_soap_free_write_buf(MsnSoapConn *soapconn);
-void msn_soap_connect_cb(gpointer data, PurpleSslConnection *gsc, PurpleInputCondition cond);
-
-/*clean the unhandled requests*/
-void msn_soap_clean_unhandled_requests(MsnSoapConn *soapconn);
-
-/*check if the soap connection is connected*/
-int msn_soap_connected(MsnSoapConn *soapconn);
-void msn_soap_set_process_step(MsnSoapConn *soapconn, MsnSoapStep step);
-
-#endif/*_MSN_SOAP_H_*/
-
+#endif
--- a/libpurple/protocols/msn/soap2.c	Wed Jul 09 00:27:44 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,680 +0,0 @@
-/**
- * @file soap2.c
- * 	C file for SOAP connection related process
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-#include "internal.h"
-
-#include "soap2.h"
-
-#include "session.h"
-
-#include "debug.h"
-#include "xmlnode.h"
-
-#include <glib.h>
-#if !defined(_WIN32) || !defined(_WINERROR_)
-#include <error.h>
-#endif
-
-#define SOAP_TIMEOUT (5 * 60)
-
-typedef struct _MsnSoapRequest {
-	char *path;
-	MsnSoapMessage *message;
-	MsnSoapCallback cb;
-	gpointer cb_data;
-} MsnSoapRequest;
-
-typedef struct _MsnSoapConnection {
-	MsnSession *session;
-	char *host;
-
-	time_t last_used;
-	PurpleSslConnection *ssl;
-	gboolean connected;
-
-	guint event_handle;
-	GString *buf;
-	gsize handled_len;
-	gsize body_len;
-	int response_code;
-	gboolean headers_done;
-	gboolean close_when_done;
-
-	MsnSoapMessage *message;
-
-	GQueue *queue;
-	MsnSoapRequest *current_request;
-} MsnSoapConnection;
-
-static void msn_soap_connection_destroy_foreach_cb(gpointer item, gpointer data);
-static gboolean msn_soap_connection_run(gpointer data);
-
-static MsnSoapConnection *msn_soap_connection_new(MsnSession *session,
-	const char *host);
-static void msn_soap_connection_handle_next(MsnSoapConnection *conn);
-static void msn_soap_connection_destroy(MsnSoapConnection *conn);
-
-static void msn_soap_message_send_internal(MsnSession *session,
-	MsnSoapMessage *message, const char *host, const char *path,
-	MsnSoapCallback cb, gpointer cb_data, gboolean first);
-
-static void msn_soap_request_destroy(MsnSoapRequest *req, gboolean keep_message);
-static void msn_soap_connection_sanitize(MsnSoapConnection *conn, gboolean disconnect);
-static gboolean msn_soap_write_cb_internal(gpointer data, gint fd, PurpleInputCondition cond, gboolean initial);
-static void msn_soap_process(MsnSoapConnection *conn);
-
-static gboolean
-msn_soap_cleanup_each(gpointer key, gpointer value, gpointer data)
-{
-	MsnSoapConnection *conn = value;
-	time_t *t = data;
-
-	if ((*t - conn->last_used) > SOAP_TIMEOUT * 2) {
-		purple_debug_info("soap", "cleaning up soap conn %p\n", conn);
-		return TRUE;
-	}
-
-	return FALSE;
-}
-
-static gboolean
-msn_soap_cleanup_for_session(gpointer data)
-{
-	MsnSession *sess = data;
-	time_t t = time(NULL);
-
-	purple_debug_info("soap", "session cleanup timeout\n");
-
-	if (sess->soap_table) {
-		g_hash_table_foreach_remove(sess->soap_table, msn_soap_cleanup_each,
-			&t);
-
-		if (g_hash_table_size(sess->soap_table) == 0) {
-			purple_timeout_remove(sess->soap_cleanup_handle);
-			sess->soap_cleanup_handle = 0;
-		}
-	}
-
-	return TRUE;
-}
-
-static MsnSoapConnection *
-msn_soap_get_connection(MsnSession *session, const char *host)
-{
-	MsnSoapConnection *conn = NULL;
-
-	if (session->soap_table) {
-		conn = g_hash_table_lookup(session->soap_table, host);
-	} else {
-		session->soap_table = g_hash_table_new_full(g_str_hash, g_str_equal,
-			NULL, (GDestroyNotify)msn_soap_connection_destroy);
-	}
-
-	if (session->soap_cleanup_handle == 0)
-		session->soap_cleanup_handle = purple_timeout_add(SOAP_TIMEOUT * 1000,
-			msn_soap_cleanup_for_session, session);
-
-	if (conn == NULL) {
-		conn = msn_soap_connection_new(session, host);
-		g_hash_table_insert(session->soap_table, conn->host, conn);
-	}
-
-	conn->last_used = time(NULL);
-
-	return conn;
-}
-
-static MsnSoapConnection *
-msn_soap_connection_new(MsnSession *session, const char *host)
-{
-	MsnSoapConnection *conn = g_new0(MsnSoapConnection, 1);
-	conn->session = session;
-	conn->host = g_strdup(host);
-	conn->queue = g_queue_new();
-	return conn;
-}
-
-static void
-msn_soap_connected_cb(gpointer data, PurpleSslConnection *ssl,
-		PurpleInputCondition cond)
-{
-	MsnSoapConnection *conn = data;
-
-	conn->connected = TRUE;
-
-	if (conn->event_handle == 0)
-		conn->event_handle = purple_timeout_add(0, msn_soap_connection_run, conn);
-}
-
-static void
-msn_soap_error_cb(PurpleSslConnection *ssl, PurpleSslErrorType error,
-		gpointer data)
-{
-	MsnSoapConnection *conn = data;
-
-	/* sslconn already frees the connection in case of error */
-	conn->ssl = NULL;
-
-	g_hash_table_remove(conn->session->soap_table, conn->host);
-}
-
-static gboolean
-msn_soap_handle_redirect(MsnSoapConnection *conn, const char *url)
-{
-	char *host;
-	char *path;
-
-	if (purple_url_parse(url, &host, NULL, &path, NULL, NULL)) {
-		msn_soap_message_send_internal(conn->session,
-			conn->current_request->message,	host, path,
-			conn->current_request->cb, conn->current_request->cb_data, TRUE);
-
-		msn_soap_request_destroy(conn->current_request, TRUE);
-		conn->current_request = NULL;
-
-		g_free(host);
-		g_free(path);
-
-		return TRUE;
-	}
-
-	return FALSE;
-}
-
-static gboolean
-msn_soap_handle_body(MsnSoapConnection *conn, MsnSoapMessage *response)
-{
-	xmlnode *body = xmlnode_get_child(response->xml, "Body");
-	xmlnode *fault = xmlnode_get_child(response->xml, "Fault");
-
-	if (fault) {
-		xmlnode *faultcode = xmlnode_get_child(fault, "faultcode");
-
-		if (faultcode != NULL) {
-			char *faultdata = xmlnode_get_data(faultcode);
-
-			if (g_str_equal(faultdata, "psf:Redirect")) {
-				xmlnode *url = xmlnode_get_child(fault, "redirectUrl");
-
-				if (url) {
-					char *urldata = xmlnode_get_data(url);
-					msn_soap_handle_redirect(conn, urldata);
-					g_free(urldata);
-				}
-
-				g_free(faultdata);
-				msn_soap_message_destroy(response);
-				return TRUE;
-			} else if (g_str_equal(faultdata, "wsse:FailedAuthentication")) {
-				xmlnode *reason = xmlnode_get_child(fault, "faultstring");
-				char *reasondata = xmlnode_get_data(reason);
-
-				msn_soap_connection_sanitize(conn, TRUE);
-				msn_session_set_error(conn->session, MSN_ERROR_AUTH,
-					reasondata);
-
-				g_free(reasondata);
-				g_free(faultdata);
-				msn_soap_message_destroy(response);
-				return FALSE;
-			}
-
-			g_free(faultdata);
-		}
-	}
-
-	if (fault || body) {
-		MsnSoapRequest *request = conn->current_request;
-		conn->current_request = NULL;
-		request->cb(request->message, response,
-			request->cb_data);
-		msn_soap_message_destroy(response);
-		msn_soap_request_destroy(request, FALSE);
-	}
-
-	return TRUE;
-}
-
-static void
-msn_soap_read_cb(gpointer data, gint fd, PurpleInputCondition cond)
-{
-	MsnSoapConnection *conn = data;
-	int count = 0, cnt, perrno;
-	/* This buffer needs to be larger than any packets received from
-		login.live.com or Adium will fail to receive the packet
-		(something weird with the login.live.com server). With NSS it works
-		fine, so I believe it's some bug with OS X */ 
-	char buf[16 * 1024];
-
-	if (conn->message == NULL) {
-		conn->message = msn_soap_message_new(NULL, NULL);
-	}
-
-	if (conn->buf == NULL) {
-		conn->buf = g_string_new_len(buf, 0);
-	}
-	
-	while ((cnt = purple_ssl_read(conn->ssl, buf, sizeof(buf))) > 0) {
-		purple_debug_info("soap", "read %d bytes\n", cnt);
-		count += cnt;
-		g_string_append_len(conn->buf, buf, cnt);
-	}
-
-	/* && count is necessary for Adium, on OS X the last read always
-	   return an error, so we want to proceed anyway. See #5212 for
-	   discussion on this and the above buffer size issues */
-	if(cnt < 0 && errno == EAGAIN && count == 0)
-		return;
-
-	// msn_soap_process could alter errno
-	perrno = errno;
-	msn_soap_process(conn);
-	
-	if (cnt < 0 && perrno != EAGAIN) {
-		purple_debug_info("soap", "read: %s\n", g_strerror(perrno));
-		// It's possible msn_soap_process closed the ssl connection
-		if (conn->ssl) {
-			purple_ssl_close(conn->ssl);
-			conn->ssl = NULL;
-			msn_soap_connection_handle_next(conn);
-		}
-	}
-}
-
-static void
-msn_soap_process(MsnSoapConnection *conn) {
-	gboolean handled = FALSE;
-	char *cursor;
-	char *linebreak;
-
-	purple_debug_info("soap", "current %s\n", conn->buf->str);
-
-	cursor = conn->buf->str + conn->handled_len;
-
-	if (!conn->headers_done) {
-		while ((linebreak = strstr(cursor, "\r\n"))	!= NULL) {
-			conn->handled_len = linebreak - conn->buf->str + 2;
-
-			if (conn->response_code == 0) {
-				if (sscanf(cursor, "HTTP/1.1 %d", &conn->response_code) != 1) {
-					/* something horribly wrong */
-					purple_ssl_close(conn->ssl);
-					conn->ssl = NULL;
-					msn_soap_connection_handle_next(conn);
-					handled = TRUE;
-					break;
-				} else if (conn->response_code == 503) {
-					msn_soap_connection_sanitize(conn, TRUE);
-					msn_session_set_error(conn->session, MSN_ERROR_SERV_UNAVAILABLE, NULL);
-					return;
-				}
-			} else if (cursor == linebreak) {
-				/* blank line */
-				conn->headers_done = TRUE;
-				cursor = conn->buf->str + conn->handled_len;
-				break;
-			} else {
-				char *line = g_strndup(cursor, linebreak - cursor);
-				char *sep = strstr(line, ": ");
-				char *key = line;
-				char *value;
-
-				if (sep == NULL) {
-					purple_debug_info("soap", "ignoring malformed line: %s\n", line);
-					g_free(line);
-					goto loop_end;
-				}
-
-				value = sep + 2;
-				*sep = '\0';
-				msn_soap_message_add_header(conn->message, key, value);
-
-				if ((conn->response_code == 301 || conn->response_code == 300)
-					&& strcmp(key, "Location") == 0) {
-
-					msn_soap_handle_redirect(conn, value);
-
-					handled = TRUE;
-					g_free(line);
-					break;
-				} else if (conn->response_code == 401 &&
-					strcmp(key, "WWW-Authenticate") == 0) {
-					char *error = strstr(value, "cbtxt=");
-
-					if (error) {
-						error += strlen("cbtxt=");
-					}
-
-					msn_soap_connection_sanitize(conn, TRUE);
-					msn_session_set_error(conn->session, MSN_ERROR_AUTH,
-						error ? purple_url_decode(error) : NULL);
-
-					g_free(line);
-					return;
-				} else if (strcmp(key, "Content-Length") == 0) {
-					conn->body_len = atoi(value);
-				} else if (strcmp(key, "Connection") == 0) {
-					if (strcmp(value, "close") == 0) {
-						conn->close_when_done = TRUE;
-					}
-				}
-				g_free(line);
-			}
-
-		loop_end:
-			cursor = conn->buf->str + conn->handled_len;
-		}
-	}
-
-	if (!handled && conn->headers_done) {
-		if (conn->buf->len - conn->handled_len >=
-			conn->body_len) {
-			xmlnode *node = xmlnode_from_str(cursor, conn->body_len);
-
-			if (node == NULL) {
-				purple_debug_info("soap", "Malformed SOAP response: %s\n",
-					cursor);
-			} else {
-				MsnSoapMessage *message = conn->message;
-				conn->message = NULL;
-				message->xml = node;
-
-				if (!msn_soap_handle_body(conn, message)) {
-					return;
-				}
-			}
-
-			msn_soap_connection_handle_next(conn);
-		}
-
-		return;
-	}
-
-	if (handled) {
-		msn_soap_connection_handle_next(conn);
-	}
-}
-
-static void
-msn_soap_write_cb(gpointer data, gint fd, PurpleInputCondition cond)
-{
-	msn_soap_write_cb_internal(data, fd, cond, FALSE);
-}
-
-static gboolean
-msn_soap_write_cb_internal(gpointer data, gint fd, PurpleInputCondition cond,
-		gboolean initial)
-{
-	MsnSoapConnection *conn = data;
-	int written;
-
-	if (cond != PURPLE_INPUT_WRITE) return TRUE;
-
-	written = purple_ssl_write(conn->ssl, conn->buf->str + conn->handled_len,
-		conn->buf->len - conn->handled_len);
-
-	if (written < 0 && errno == EAGAIN)
-		return TRUE;
-	else if (written <= 0) {
-		purple_ssl_close(conn->ssl);
-		conn->ssl = NULL;
-		if (!initial) msn_soap_connection_handle_next(conn);
-		return FALSE;
-	}
-
-	conn->handled_len += written;
-
-	if (conn->handled_len < conn->buf->len)
-		return TRUE;
-
-	/* we are done! */
-	g_string_free(conn->buf, TRUE);
-	conn->buf = NULL;
-	conn->handled_len = 0;
-	conn->body_len = 0;
-	conn->response_code = 0;
-	conn->headers_done = FALSE;
-	conn->close_when_done = FALSE;
-
-	purple_input_remove(conn->event_handle);
-	conn->event_handle = purple_input_add(conn->ssl->fd, PURPLE_INPUT_READ,
-		msn_soap_read_cb, conn);
-	return TRUE;
-}
-
-static gboolean
-msn_soap_connection_run(gpointer data)
-{
-	MsnSoapConnection *conn = data;
-	MsnSoapRequest *req = g_queue_peek_head(conn->queue);
-
-	conn->event_handle = 0;
-
-	if (req) {
-		if (conn->ssl == NULL) {
-			conn->ssl = purple_ssl_connect(conn->session->account, conn->host,
-				443, msn_soap_connected_cb, msn_soap_error_cb, conn);
-		} else if (conn->connected) {
-			int len = -1;
-			char *body = xmlnode_to_str(req->message->xml, &len);
-			GSList *iter;
-
-			g_queue_pop_head(conn->queue);
-
-			conn->buf = g_string_new("");
-
-			g_string_append_printf(conn->buf,
-				"POST /%s HTTP/1.1\r\n"
-				"SOAPAction: %s\r\n"
-				"Content-Type:text/xml; charset=utf-8\r\n"
-				"User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)\r\n"
-				"Accept: */*\r\n"
-				"Host: %s\r\n"
-				"Content-Length: %d\r\n"
-				"Connection: Keep-Alive\r\n"
-				"Cache-Control: no-cache\r\n",
-				req->path, req->message->action ? req->message->action : "",
-				conn->host, len);
-
-			for (iter = req->message->headers; iter; iter = iter->next) {
-				g_string_append(conn->buf, (char *)iter->data);
-				g_string_append(conn->buf, "\r\n");
-			}
-
-			g_string_append(conn->buf, "\r\n");
-			g_string_append(conn->buf, body);
-
-			purple_debug_info("soap", "%s\n", conn->buf->str);
-
-			conn->handled_len = 0;
-			conn->current_request = req;
-
-			conn->event_handle = purple_input_add(conn->ssl->fd,
-				PURPLE_INPUT_WRITE, msn_soap_write_cb, conn);
-			if (!msn_soap_write_cb_internal(conn, conn->ssl->fd, PURPLE_INPUT_WRITE, TRUE)) {
-				/* Not connected => reconnect and retry */
-				purple_debug_info("soap", "not connected, reconnecting");
-				
-				conn->connected = FALSE;
-				conn->current_request = NULL;
-				msn_soap_connection_sanitize(conn, FALSE);
-				
-				g_queue_push_head(conn->queue, req);
-				conn->event_handle = purple_timeout_add(0, msn_soap_connection_run, conn);
-			}
-
-			g_free(body);
-		}
-	}
-
-	return FALSE;
-}
-
-void
-msn_soap_message_send(MsnSession *session, MsnSoapMessage *message,
-	const char *host, const char *path,
-	MsnSoapCallback cb, gpointer cb_data)
-{
-	msn_soap_message_send_internal(session, message, host, path, cb, cb_data,
-		FALSE);
-}
-
-static void
-msn_soap_message_send_internal(MsnSession *session,
-	MsnSoapMessage *message, const char *host, const char *path,
-	MsnSoapCallback cb, gpointer cb_data, gboolean first)
-{
-	MsnSoapConnection *conn = msn_soap_get_connection(session, host);
-	MsnSoapRequest *req = g_new0(MsnSoapRequest, 1);
-
-	req->path = g_strdup(path);
-	req->message = message;
-	req->cb = cb;
-	req->cb_data = cb_data;
-
-	if (first) {
-		g_queue_push_head(conn->queue, req);
-	} else {
-		g_queue_push_tail(conn->queue, req);
-	}
-
-	if (conn->event_handle == 0)
-		conn->event_handle = purple_timeout_add(0, msn_soap_connection_run,
-			conn);
-}
-
-static void
-msn_soap_connection_sanitize(MsnSoapConnection *conn, gboolean disconnect)
-{
-	if (conn->event_handle) {
-		purple_input_remove(conn->event_handle);
-		conn->event_handle = 0;
-	}
-
-	if (conn->message) {
-		msn_soap_message_destroy(conn->message);
-		conn->message = NULL;
-	}
-
-	if (conn->buf) {
-		g_string_free(conn->buf, TRUE);
-		conn->buf = NULL;
-	}
-
-	if (conn->ssl && (disconnect || conn->close_when_done)) {
-		purple_ssl_close(conn->ssl);
-		conn->ssl = NULL;
-	}
-
-	if (conn->current_request) {
-		msn_soap_request_destroy(conn->current_request, FALSE);
-		conn->current_request = NULL;
-	}
-}
-
-static void
-msn_soap_connection_handle_next(MsnSoapConnection *conn)
-{
-	msn_soap_connection_sanitize(conn, FALSE);
-
-	conn->event_handle = purple_timeout_add(0, msn_soap_connection_run,	conn);
-
-	if (conn->current_request) {
-		MsnSoapRequest *req = conn->current_request;
-		conn->current_request = NULL;
-		msn_soap_connection_destroy_foreach_cb(req, conn);
-	}
-}
-
-static void
-msn_soap_connection_destroy_foreach_cb(gpointer item, gpointer data)
-{
-	MsnSoapRequest *req = item;
-
-	if (req->cb)
-		req->cb(req->message, NULL, req->cb_data);
-
-	msn_soap_request_destroy(req, FALSE);
-}
-
-static void
-msn_soap_connection_destroy(MsnSoapConnection *conn)
-{
-	if (conn->current_request) {
-		MsnSoapRequest *req = conn->current_request;
-		conn->current_request = NULL;
-		msn_soap_connection_destroy_foreach_cb(req, conn);
-	}
-
-	msn_soap_connection_sanitize(conn, TRUE);
-	g_queue_foreach(conn->queue, msn_soap_connection_destroy_foreach_cb, conn);
-	g_queue_free(conn->queue);
-
-	g_free(conn->host);
-	g_free(conn);
-}
-
-MsnSoapMessage *
-msn_soap_message_new(const char *action, xmlnode *xml)
-{
-	MsnSoapMessage *message = g_new0(MsnSoapMessage, 1);
-
-	message->action = g_strdup(action);
-	message->xml = xml;
-
-	return message;
-}
-
-void
-msn_soap_message_destroy(MsnSoapMessage *message)
-{
-	if (message) {
-		g_slist_foreach(message->headers, (GFunc)g_free, NULL);
-		g_slist_free(message->headers);
-		g_free(message->action);
-		if (message->xml)
-			xmlnode_free(message->xml);
-		g_free(message);
-	}
-}
-
-void
-msn_soap_message_add_header(MsnSoapMessage *message,
-		const char *name, const char *value)
-{
-	char *header = g_strdup_printf("%s: %s\r\n", name, value);
-
-	message->headers = g_slist_prepend(message->headers, header);
-}
-
-static void
-msn_soap_request_destroy(MsnSoapRequest *req, gboolean keep_message)
-{
-	g_free(req->path);
-	if (!keep_message)
-		msn_soap_message_destroy(req->message);
-	g_free(req);
-}
-
--- a/libpurple/protocols/msn/soap2.h	Wed Jul 09 00:27:44 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,56 +0,0 @@
-/**
- * @file soap2.h
- * 	header file for SOAP connection related process
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-#ifndef _MSN_SOAP2_H
-#define _MSN_SOAP2_H
-
-#include "session.h"
-#include "sslconn.h"
-#include "xmlnode.h"
-
-#include <glib.h>
-
-typedef struct _MsnSoapMessage MsnSoapMessage;
-typedef void (*MsnSoapCallback)(MsnSoapMessage *request,
-	MsnSoapMessage *response, gpointer cb_data);
-
-struct _MsnSoapMessage {
-	char *action;
-	xmlnode *xml;
-	GSList *headers;
-};
-
-MsnSoapMessage *msn_soap_message_new(const char *action, xmlnode *xml);
-
-void msn_soap_message_add_header(MsnSoapMessage *req,
-	const char *name, const char *value);
-
-void msn_soap_message_send(MsnSession *session,
-	MsnSoapMessage *message, const char *host, const char *path,
-	MsnSoapCallback cb, gpointer cb_data);
-
-void msn_soap_message_destroy(MsnSoapMessage *message);
-
-#endif
--- a/libpurple/protocols/msn/switchboard.c	Wed Jul 09 00:27:44 2008 +0000
+++ b/libpurple/protocols/msn/switchboard.c	Wed Jul 09 00:32:18 2008 +0000
@@ -960,17 +960,40 @@
 }
 
 static void
-nudge_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
+datacast_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
 {
-	MsnSwitchBoard *swboard;
-	PurpleAccount *account;
-	const char *user;
+	GHashTable *body;
+	const char *id;
+	body = msn_message_get_hashtable_from_body(msg);
+
+	id = g_hash_table_lookup(body, "ID");
+
+	if (!strcmp(id, "1")) {
+		/* Nudge */
+		MsnSwitchBoard *swboard;
+		PurpleAccount *account;
+		const char *user;
 
-	swboard = cmdproc->data;
-	account = cmdproc->session->account;
-	user = msg->remote_user;
+		swboard = cmdproc->data;
+		account = cmdproc->session->account;
+		user = msg->remote_user;
+
+		serv_got_attention(account->gc, user, MSN_NUDGE);
+
+	} else if (!strcmp(id, "2")) {
+		/* Wink */
 
-	serv_got_attention(account->gc, user, MSN_NUDGE);
+	} else if (!strcmp(id, "3")) {
+		/* Voiceclip */
+
+	} else if (!strcmp(id, "4")) {
+		/* Action */
+
+	} else {
+		purple_debug_warning("msn", "Got unknown datacast with ID %s.\n", id);
+	}
+
+	g_hash_table_destroy(body);
 }
 
 /**************************************************************************
@@ -1059,7 +1082,7 @@
 	msn_servconn_set_connect_cb(swboard->servconn, connect_cb);
 	msn_servconn_set_disconnect_cb(swboard->servconn, disconnect_cb);
 
-	return msn_servconn_connect(swboard->servconn, host, port);
+	return msn_servconn_connect(swboard->servconn, host, port, FALSE);
 }
 
 void
@@ -1114,13 +1137,9 @@
 	}
 
 	purple_debug_warning("msn", "cal_error: command %s gave error %i\n", trans->command, error);
-	purple_debug_warning("msn", "Will Use Offline Message to sendit\n");
-
-//	cal_error_helper(trans, reason);
-	/*offline Message send Process*/
 
 	while ((msg = g_queue_pop_head(swboard->msg_queue)) != NULL){
-		purple_debug_warning("MSNP14","offline msg to send:{%s}\n",msg->body);
+		purple_debug_warning("MSNP14", "Unable to send msg: {%s}\n", msg->body);
 		/* The messages could not be sent due to a switchboard error */
 		swboard->error = MSN_SB_ERROR_USER_OFFLINE;
 		msg_error_helper(swboard->cmdproc, msg,
@@ -1313,7 +1332,7 @@
 	msn_table_add_msg_type(cbs_table, "text/x-mms-animemoticon",
 	                                           msn_emoticon_msg);
 	msn_table_add_msg_type(cbs_table, "text/x-msnmsgr-datacast",
-						   nudge_msg);
+						   datacast_msg);
 #if 0
 	msn_table_add_msg_type(cbs_table, "text/x-msmmsginvite",
 						   msn_invite_msg);
--- a/libpurple/protocols/msnp9/slplink.c	Wed Jul 09 00:27:44 2008 +0000
+++ b/libpurple/protocols/msnp9/slplink.c	Wed Jul 09 00:32:18 2008 +0000
@@ -597,7 +597,7 @@
 	}
 	else if (slpmsg->size)
 	{
-		if ((offset + len) > slpmsg->size)
+		if (G_MAXSIZE - len < offset || (offset + len) > slpmsg->size)
 		{
 			purple_debug_error("msn", "Oversized slpmsg\n");
 			g_return_if_reached();
--- a/libpurple/protocols/oscar/family_auth.c	Wed Jul 09 00:27:44 2008 +0000
+++ b/libpurple/protocols/oscar/family_auth.c	Wed Jul 09 00:32:18 2008 +0000
@@ -81,12 +81,9 @@
 static int
 aim_encode_password_md5(const char *password, size_t password_len, const char *key, guint8 *digest)
 {
-	PurpleCipher *cipher;
 	PurpleCipherContext *context;
 
-	cipher = purple_ciphers_find_cipher("md5");
-
-	context = purple_cipher_context_new(cipher, NULL);
+	context = purple_cipher_context_new_by_name("md5", NULL);
 	purple_cipher_context_append(context, (const guchar *)key, strlen(key));
 	purple_cipher_context_append(context, (const guchar *)password, password_len);
 	purple_cipher_context_append(context, (const guchar *)AIM_MD5_STRING, strlen(AIM_MD5_STRING));
--- a/libpurple/protocols/oscar/family_oservice.c	Wed Jul 09 00:27:44 2008 +0000
+++ b/libpurple/protocols/oscar/family_oservice.c	Wed Jul 09 00:32:18 2008 +0000
@@ -952,13 +952,10 @@
 		byte_stream_putraw(&bs, buf, 0x10);
 
 	} else if (buf && (len > 0)) { /* use input buffer */
-		PurpleCipher *cipher;
 		PurpleCipherContext *context;
 		guchar digest[16];
 
-		cipher = purple_ciphers_find_cipher("md5");
-
-		context = purple_cipher_context_new(cipher, NULL);
+		context = purple_cipher_context_new_by_name("md5", NULL);
 		purple_cipher_context_append(context, buf, len);
 		purple_cipher_context_digest(context, 16, digest, NULL);
 		purple_cipher_context_destroy(context);
@@ -966,7 +963,6 @@
 		byte_stream_putraw(&bs, digest, 0x10);
 
 	} else if (len == 0) { /* no length, just hash NULL (buf is optional) */
-		PurpleCipher *cipher;
 		PurpleCipherContext *context;
 		guchar digest[16];
 		guint8 nil = '\0';
@@ -975,9 +971,7 @@
 		 * I'm not sure if we really need the empty append with the
 		 * new MD5 functions, so I'll leave it in, just in case.
 		 */
-		cipher = purple_ciphers_find_cipher("md5");
-
-		context = purple_cipher_context_new(cipher, NULL);
+		context = purple_cipher_context_new_by_name("md5", NULL);
 		purple_cipher_context_append(context, &nil, 0);
 		purple_cipher_context_digest(context, 16, digest, NULL);
 		purple_cipher_context_destroy(context);
--- a/libpurple/protocols/oscar/oscar.c	Wed Jul 09 00:27:44 2008 +0000
+++ b/libpurple/protocols/oscar/oscar.c	Wed Jul 09 00:32:18 2008 +0000
@@ -6424,15 +6424,12 @@
 	if (img == NULL) {
 		aim_ssi_delicon(od);
 	} else {
-		PurpleCipher *cipher;
 		PurpleCipherContext *context;
 		guchar md5[16];
 		gconstpointer data = purple_imgstore_get_data(img);
 		size_t len = purple_imgstore_get_size(img);
 
-
-		cipher = purple_ciphers_find_cipher("md5");
-		context = purple_cipher_context_new(cipher, NULL);
+		context = purple_cipher_context_new_by_name("md5", NULL);
 		purple_cipher_context_append(context, data, len);
 		purple_cipher_context_digest(context, 16, md5, NULL);
 		purple_cipher_context_destroy(context);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/qq/ChangeLog	Wed Jul 09 00:32:18 2008 +0000
@@ -0,0 +1,49 @@
+2008.06.07 - ccpaging <ecc_hy(at)hotmail.com>, csyfek <csyfek(at)gmail.com>
+	* Clean code and apply patches from QuLogic
+
+2008.05.19 - ccpaging <ecc_hy(at)hotmail.com>, csyfek <csyfek(at)gmail.com>
+	* Reconnect server 5 time in 5000 ms, when connect failed
+	* Rename sendqueue.c/sendqueue.h to qq_trans.c/qq_trans.h
+	* Rewrite packet_process
+	* Rewrite qq_send_cmd
+	* Create server list, try to connect every server when failed
+
+2008.05.14 - ccpaging <ecc_hy(at)hotmail.com>
+	* Move function for before login packets storing to sendqueue
+	* Use transaction data structure to store before login packets
+	* Rewrite tcp_pending and packet_process in qq_network.c
+
+2008.05.09 - ccpaging <ecc_hy(at)hotmail.com>
+	* Remove function _create_packet_head_seq in qq_network.c
+	* Create new function encap in qq_netowork.c
+	* Clean code of qq_send_packet_request_login_token and qq_send_packet_login in login_out.c
+
+2008.05.09 - ccpaging <ecc_hy(at)hotmail.com>
+	* Clean code of packet_parse.c, enable PARSER_DEBUG
+	* Rewrite send_queue
+
+2008.05.08 - ccpaging <ecc_hy(at)hotmail.com>
+	* Rewrite qq_network
+	* Add srv resolve function when qq_login
+	* Merge function _qq_common_clean in qq_proxy.c to qq_disconnect
+	* Move orignal qq_disconnect to qq_close
+	* qq_data alloc in qq_open and release in qq_close
+	* Network connect of QQ is created in qq_connect, and release in qq_disconnect
+
+2008.05.05 - ccpaging <ecc_hy(at)hotmail.com>
+	* Merge function _qq_common_clean in qq_proxy.c to qq_disconnect
+	* Move orignal qq_disconnect to qq_close
+	* qq_data alloc in qq_open and release in qq_close
+	* Network connect of QQ is created in qq_connect, and release in qq_disconnect
+
+2008.05.05 - ccpaging <ecc_hy(at)hotmail.com>
+	* Add qq_hex_dump function
+
+2008.04.25 - ccpaging <ecc_hy(at)hotmail.com>, csyfek <csyfek(at)gmail.com>
+	* Rewrite read_packet and create_packet functions, use qq_put and qq_get functions instead
+	* New logic in accord with protocol models to handle packets, some related functions rewritten
+
+2008.03.24 - ccpaging <ecc_hy(at)hotmail.com>
+	* Remove qq_crypt function in crypt.c, use qq_crypt and qq_decrypt directly
+
+** since pidgin-2.4.0 ***
--- a/libpurple/protocols/qq/Makefile.am	Wed Jul 09 00:27:44 2008 +0000
+++ b/libpurple/protocols/qq/Makefile.am	Wed Jul 09 00:32:18 2008 +0000
@@ -52,20 +52,14 @@
 	packet_parse.h \
 	qq.c \
 	qq.h \
-	qq_proxy.c \
-	qq_proxy.h \
-	recv_core.c \
-	recv_core.h \
-	send_core.c \
-	send_core.h \
+	qq_network.c \
+	qq_network.h \
 	send_file.c \
 	send_file.h \
-	sendqueue.c \
-	sendqueue.h \
+	qq_trans.c \
+	qq_trans.h \
 	sys_msg.c \
 	sys_msg.h \
-	udp_proxy_s5.c \
-	udp_proxy_s5.h \
 	utils.c \
 	utils.h
 
--- a/libpurple/protocols/qq/Makefile.mingw	Wed Jul 09 00:27:44 2008 +0000
+++ b/libpurple/protocols/qq/Makefile.mingw	Wed Jul 09 00:32:18 2008 +0000
@@ -63,13 +63,10 @@
 	login_logout.c \
 	packet_parse.c \
 	qq.c \
-	qq_proxy.c \
-	recv_core.c \
-	send_core.c \
+	qq_network.c \
 	send_file.c \
-	sendqueue.c \
+	qq_trans.c \
 	sys_msg.c \
-	udp_proxy_s5.c \
 	utils.c
 
 OBJECTS = $(C_SRC:%.c=%.o)
--- a/libpurple/protocols/qq/buddy_info.c	Wed Jul 09 00:27:44 2008 +0000
+++ b/libpurple/protocols/qq/buddy_info.c	Wed Jul 09 00:32:18 2008 +0000
@@ -34,7 +34,7 @@
 #include "crypt.h"
 #include "header_info.h"
 #include "keep_alive.h"
-#include "send_core.h"
+#include "qq_network.h"
 
 #define QQ_PRIMARY_INFORMATION _("Primary Information")
 #define QQ_ADDITIONAL_INFORMATION _("Additional Information")
@@ -94,6 +94,46 @@
 	gboolean modify_info;
 } qq_info_query;
 
+typedef struct _contact_info {
+	gchar *uid;
+	gchar *nick;
+	gchar *country;
+	gchar *province;
+	gchar *zipcode;
+	gchar *address;
+	gchar *tel;
+	gchar *age;
+	gchar *gender;
+	gchar *name;
+	gchar *email;
+	gchar *pager_sn;
+	gchar *pager_num;
+	gchar *pager_sp;
+	gchar *pager_base_num;
+	gchar *pager_type;
+	gchar *occupation;
+	gchar *homepage;
+	gchar *auth_type;
+	gchar *unknown1;
+	gchar *unknown2;
+	gchar *face;
+	gchar *hp_num;
+	gchar *hp_type;
+	gchar *intro;
+	gchar *city;
+	gchar *unknown3;
+	gchar *unknown4;
+	gchar *unknown5;
+	gchar *is_open_hp;
+	gchar *is_open_contact;
+	gchar *college;
+	gchar *horoscope;
+	gchar *zodiac;
+	gchar *blood;
+	gchar *qq_show;
+	gchar *unknown6;        /* always 0x2D */
+} contact_info;
+
 /* We get an info packet on ourselves before we modify our information.
  * Even though not all of the information is modifiable, it still
  * all needs to be there when we send out the modify info packet */
@@ -137,7 +177,7 @@
 			} else {
 				return NULL;
 			}
-		/* else ASCIIized index */
+			/* else ASCIIized index */
 		} else {
 			if (strcmp(choice[index], "-") != 0)
 				return g_strdup(choice[index]);
@@ -161,14 +201,14 @@
 	if (value != NULL) {
 		purple_notify_user_info_add_pair(user_info, title, value);
 		g_free(value);
-		
+
 		return TRUE;
 	}
-	
+
 	return FALSE;
 }
 
-static PurpleNotifyUserInfo *
+	static PurpleNotifyUserInfo *
 info_to_notify_user_info(const contact_info *info)
 {
 	PurpleNotifyUserInfo *user_info = purple_notify_user_info_new();
@@ -209,25 +249,25 @@
 
 	/* for debugging */
 	/*
-	g_string_append_printf(info_text, "<br /><br /><b>%s</b><br />", "Miscellaneous");
-	append_field_value(info_text, info->pager_sn, "pager_sn", NULL, 0);
-	append_field_value(info_text, info->pager_num, "pager_num", NULL, 0);
-	append_field_value(info_text, info->pager_sp, "pager_sp", NULL, 0);
-	append_field_value(info_text, info->pager_base_num, "pager_base_num", NULL, 0);
-	append_field_value(info_text, info->pager_type, "pager_type", NULL, 0);
-	append_field_value(info_text, info->auth_type, "auth_type", NULL, 0);
-	append_field_value(info_text, info->unknown1, "unknown1", NULL, 0);
-	append_field_value(info_text, info->unknown2, "unknown2", NULL, 0);
-	append_field_value(info_text, info->face, "face", NULL, 0);
-	append_field_value(info_text, info->hp_type, "hp_type", NULL, 0);
-	append_field_value(info_text, info->unknown3, "unknown3", NULL, 0);
-	append_field_value(info_text, info->unknown4, "unknown4", NULL, 0);
-	append_field_value(info_text, info->unknown5, "unknown5", NULL, 0);
-	append_field_value(info_text, info->is_open_hp, "is_open_hp", NULL, 0);
-	append_field_value(info_text, info->is_open_contact, "is_open_contact", NULL, 0);
-	append_field_value(info_text, info->qq_show, "qq_show", NULL, 0);
-	append_field_value(info_text, info->unknown6, "unknown6", NULL, 0);
-	*/
+	   g_string_append_printf(info_text, "<br /><br /><b>%s</b><br />", "Miscellaneous");
+	   append_field_value(info_text, info->pager_sn, "pager_sn", NULL, 0);
+	   append_field_value(info_text, info->pager_num, "pager_num", NULL, 0);
+	   append_field_value(info_text, info->pager_sp, "pager_sp", NULL, 0);
+	   append_field_value(info_text, info->pager_base_num, "pager_base_num", NULL, 0);
+	   append_field_value(info_text, info->pager_type, "pager_type", NULL, 0);
+	   append_field_value(info_text, info->auth_type, "auth_type", NULL, 0);
+	   append_field_value(info_text, info->unknown1, "unknown1", NULL, 0);
+	   append_field_value(info_text, info->unknown2, "unknown2", NULL, 0);
+	   append_field_value(info_text, info->face, "face", NULL, 0);
+	   append_field_value(info_text, info->hp_type, "hp_type", NULL, 0);
+	   append_field_value(info_text, info->unknown3, "unknown3", NULL, 0);
+	   append_field_value(info_text, info->unknown4, "unknown4", NULL, 0);
+	   append_field_value(info_text, info->unknown5, "unknown5", NULL, 0);
+	   append_field_value(info_text, info->is_open_hp, "is_open_hp", NULL, 0);
+	   append_field_value(info_text, info->is_open_contact, "is_open_contact", NULL, 0);
+	   append_field_value(info_text, info->qq_show, "qq_show", NULL, 0);
+	   append_field_value(info_text, info->unknown6, "unknown6", NULL, 0);
+	   */
 
 	return user_info;
 }
@@ -243,7 +283,7 @@
 
 	qd = (qq_data *) gc->proto_data;
 	g_snprintf(uid_str, sizeof(uid_str), "%d", uid);
-	qq_send_cmd(gc, QQ_CMD_GET_USER_INFO, TRUE, 0, TRUE, (guint8 *) uid_str, strlen(uid_str));
+	qq_send_cmd(qd, QQ_CMD_GET_USER_INFO, (guint8 *) uid_str, strlen(uid_str));
 
 	query = g_new0(qq_info_query, 1);
 	query->uid = uid;
@@ -271,27 +311,141 @@
 }
 
 /* send packet to modify personal information */
-static void qq_send_packet_modify_info(PurpleConnection *gc, gchar **segments)
+static void qq_send_packet_modify_info(PurpleConnection *gc, contact_info *info)
 {
-	gint i;
-	guint8 *raw_data, *cursor, bar;
+	qq_data *qd = (qq_data *) gc->proto_data;
+	gint bytes = 0;
+	guint8 raw_data[MAX_PACKET_SIZE - 128] = {0};
+	guint8 bar;
 
-	g_return_if_fail(segments != NULL);
+	g_return_if_fail(info != NULL);
 
 	bar = 0x1f;
-	raw_data = g_newa(guint8, MAX_PACKET_SIZE - 128);
-	cursor = raw_data;
 
-	create_packet_b(raw_data, &cursor, bar);
+	bytes += qq_put8(raw_data + bytes, bar);
 
 	/* important! skip the first uid entry */
-	for (i = 1; i < QQ_CONTACT_FIELDS; i++) {
-		create_packet_b(raw_data, &cursor, bar);
-		create_packet_data(raw_data, &cursor, (guint8 *) segments[i], strlen(segments[i]));
-	}
-	create_packet_b(raw_data, &cursor, bar);
+	/*
+	   for (i = 1; i < QQ_CONTACT_FIELDS; i++) {
+	   create_packet_b(raw_data, &cursor, bar);
+	   create_packet_data(raw_data, &cursor, (guint8 *) segments[i], strlen(segments[i]));
+	   }
+	   */
+	/* uid */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->uid, strlen(info->uid));
+	/* nick */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->nick, strlen(info->nick));
+	/* country */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->country, strlen(info->country));
+	/* province */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->province, strlen(info->province));
+	/* zipcode */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->zipcode, strlen(info->zipcode));
+	/* address */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->address, strlen(info->address));
+	/* tel */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->tel, strlen(info->tel));
+	/* age */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->age, strlen(info->age));
+	/* gender */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->gender, strlen(info->gender));
+	/* name */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->name, strlen(info->name));
+	/* email */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->email, strlen(info->email));
+	/* pager_sn */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->pager_sn, strlen(info->pager_sn));
+	/* pager_num */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->pager_num, strlen(info->pager_num));
+	/* pager_sp */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->pager_sp, strlen(info->pager_sp));
+	/* pager_base_num */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->pager_base_num, strlen(info->pager_base_num));
+	/* pager_type */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->pager_type, strlen(info->pager_type));
+	/* occupation */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->occupation, strlen(info->occupation));
+	/* homepage */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->homepage, strlen(info->homepage));
+	/* auth_type */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->auth_type, strlen(info->auth_type));
+	/* unknown1 */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->unknown1, strlen(info->unknown1));
+	/* unknown2 */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->unknown2, strlen(info->unknown2));
+	/* face */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->face, strlen(info->face));
+	/* hp_num */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->hp_num, strlen(info->hp_num));
+	/* hp_type */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->hp_type, strlen(info->hp_type));
+	/* intro */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->intro, strlen(info->intro));
+	/* city */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->city, strlen(info->city));
+	/* unknown3 */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->unknown3, strlen(info->unknown3));
+	/* unknown4 */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->unknown4, strlen(info->unknown4));
+	/* unknown5 */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->unknown5, strlen(info->unknown5));
+	/* is_open_hp */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->is_open_hp, strlen(info->is_open_hp));
+	/* is_open_contact */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->is_open_contact, strlen(info->is_open_contact));
+	/* college */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->college, strlen(info->college));
+	/* horoscope */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->horoscope, strlen(info->horoscope));
+	/* zodiac */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->zodiac, strlen(info->zodiac));
+	/* blood */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->blood, strlen(info->blood));
+	/* qq_show */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->qq_show, strlen(info->qq_show));
+	/* unknown6 */
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *)info->unknown6, strlen(info->unknown6));
 
-	qq_send_cmd(gc, QQ_CMD_UPDATE_INFO, TRUE, 0, TRUE, raw_data, cursor - raw_data);
+	bytes += qq_put8(raw_data + bytes, bar);
+
+	qq_send_cmd(qd, QQ_CMD_UPDATE_INFO, raw_data, bytes);
 
 }
 
@@ -407,8 +561,11 @@
 		groups = groups->next;
 	}
 
-	/* This casting looks like a horrible idea to me -DAA */
-	qq_send_packet_modify_info(gc, (gchar **) info);
+	/* This casting looks like a horrible idea to me -DAA
+	 * yes, rewritten -s3e
+	 * qq_send_packet_modify_info(gc, (gchar **) info);
+	 */
+	qq_send_packet_modify_info(gc, info);
 
 	g_strfreev((gchar **) mid->info);
 	g_free(mid);
@@ -520,11 +677,11 @@
 		mid->info->unknown6 = g_strdup(info->unknown6);
 
 		purple_request_fields(gc, _("Modify my information"),
-			_("Modify my information"), NULL, fields,
-			_("Update my information"), G_CALLBACK(modify_info_ok_cb),
-			_("Cancel"), G_CALLBACK(modify_info_cancel_cb),
-			purple_connection_get_account(gc), NULL, NULL,
-			mid);
+				_("Modify my information"), NULL, fields,
+				_("Update my information"), G_CALLBACK(modify_info_ok_cb),
+				_("Cancel"), G_CALLBACK(modify_info_cancel_cb),
+				purple_connection_get_account(gc), NULL, NULL,
+				mid);
 	}
 }
 
@@ -578,10 +735,9 @@
 	gchar *data;
 	gsize len;
 
-	if (!g_file_get_contents(iconfile, &data, &len, NULL))
+	if (!g_file_get_contents(iconfile, &data, &len, NULL)) {
 		g_return_if_reached();
-	else
-	{
+	} else {
 		purple_buddy_icons_set_for_user(account, who, data, len, icon_num);
 	}
 }
@@ -608,10 +764,10 @@
 
 	/* make sure we're using an appropriate icon */
 	if (!(g_ascii_strncasecmp(icon_path, buddy_icon_dir, dir_len) == 0
-		&& icon_path[dir_len] == G_DIR_SEPARATOR
-			&& g_ascii_strncasecmp(icon_path + dir_len + 1, QQ_ICON_PREFIX, prefix_len) == 0
-			&& g_ascii_strncasecmp(icon_path + dir_len + 1 + prefix_len + icon_len, QQ_ICON_SUFFIX, suffix_len) == 0
-			&& icon_len <= 3)) {
+				&& icon_path[dir_len] == G_DIR_SEPARATOR
+				&& g_ascii_strncasecmp(icon_path + dir_len + 1, QQ_ICON_PREFIX, prefix_len) == 0
+				&& g_ascii_strncasecmp(icon_path + dir_len + 1 + prefix_len + icon_len, QQ_ICON_SUFFIX, suffix_len) == 0
+				&& icon_len <= 3)) {
 		if (icon_global)
 			purple_debug(PURPLE_DEBUG_ERROR, "QQ", "%s\n", errmsg);
 		else
@@ -650,13 +806,13 @@
 		old_icon_num = purple_buddy_icons_get_checksum_for_user(buddy);
 
 	if (old_icon_num == NULL ||
-	    strcmp(icon_num_str, old_icon_num))
+			strcmp(icon_num_str, old_icon_num))
 	{
 		gchar *icon_path;
 
 		icon_path = g_strconcat(qq_buddy_icon_dir(), G_DIR_SEPARATOR_S,
-		                        QQ_ICON_PREFIX, icon_num_str,
-		                        QQ_ICON_SUFFIX, NULL);
+				QQ_ICON_PREFIX, icon_num_str,
+				QQ_ICON_SUFFIX, NULL);
 
 		qq_set_buddy_icon_for_user(account, name, icon_num_str, icon_path);
 		g_free(icon_path);
@@ -665,7 +821,7 @@
 }
 
 /* after getting info or modify myself, refresh the buddy list accordingly */
-void qq_refresh_buddy_and_myself(contact_info *info, PurpleConnection *gc)
+static void qq_refresh_buddy_and_myself(contact_info *info, PurpleConnection *gc)
 {
 	PurpleBuddy *b;
 	qq_data *qd;
@@ -728,7 +884,7 @@
 			qd->modifying_face = FALSE;
 			g_free(info->face);
 			info->face = icon;
-			qq_send_packet_modify_info(gc, segments);
+			qq_send_packet_modify_info(gc, (contact_info *)segments);
 		}
 
 		qq_refresh_buddy_and_myself(info, gc);
@@ -777,39 +933,41 @@
 
 void qq_send_packet_get_level(PurpleConnection *gc, guint32 uid)
 {
-	guint8 buf[5];
-	guint32 tmp = g_htonl(uid);
-	buf[0] = 0;
-	memcpy(buf+1, &tmp, 4);
-	qq_send_cmd(gc, QQ_CMD_GET_LEVEL, TRUE, 0, TRUE, buf, 5);
+	qq_data *qd = (qq_data *) gc->proto_data;
+	guint8 buf[16] = {0};
+	gint bytes = 0;
+
+	bytes += qq_put8(buf + bytes, 0x00);
+	bytes += qq_put32(buf + bytes, uid);
+
+	qd = (qq_data *) gc->proto_data;
+	qq_send_cmd(qd, QQ_CMD_GET_LEVEL, buf, bytes);
 }
 
 void qq_send_packet_get_buddies_levels(PurpleConnection *gc)
 {
-	guint8 *buf, *tmp;
+	guint8 *buf;
 	guint16 size;
 	qq_buddy *q_bud;
 	qq_data *qd = (qq_data *) gc->proto_data;
 	GList *node = qd->buddies;
+	gint bytes = 0;
 
 	if (qd->buddies) {
 		/* server only sends back levels for online buddies, no point
- 	 	* in asking for anyone else */
-		size = 4*g_list_length(qd->buddies) + 1;
+		 * in asking for anyone else */
+		size = 4 * g_list_length(qd->buddies) + 1;
 		buf = g_new0(guint8, size);
-		tmp = buf + 1;
+		bytes += 1;
 
-		while (node != NULL) {
-			guint32 tmp4;
+		while (NULL != node) {
 			q_bud = (qq_buddy *) node->data;
-			if (q_bud != NULL) {
-				tmp4 = g_htonl(q_bud->uid);
-				memcpy(tmp, &tmp4, 4);
-				tmp += 4;
+			if (NULL != q_bud) {
+				bytes += qq_put32(buf + bytes, q_bud->uid);
 			}
 			node = node->next;
 		}
-		qq_send_cmd(gc, QQ_CMD_GET_LEVEL, TRUE, 0, TRUE, buf, size);
+		qq_send_cmd(qd, QQ_CMD_GET_LEVEL, buf, size);
 		g_free(buf);
 	}
 }
@@ -822,10 +980,11 @@
 	PurpleBuddy *b;
 	qq_buddy *q_bud;
 	gint decr_len, i;
-	guint8 *decr_buf, *tmp;
+	guint8 *decr_buf;
 	PurpleAccount *account = purple_connection_get_account(gc);
 	qq_data *qd = (qq_data *) gc->proto_data;
-	
+	gint bytes = 0;
+
 	decr_len = buf_len;
 	decr_buf = g_new0(guint8, buf_len);
 	if (!qq_decrypt(buf, buf_len, qd->session_key, decr_buf, &decr_len)) {
@@ -835,28 +994,23 @@
 	decr_len--; 
 	if (decr_len % 12 != 0) {
 		purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
-			"Get levels list of abnormal length. Truncating last %d bytes.\n", decr_len % 12);
+				"Get levels list of abnormal length. Truncating last %d bytes.\n", decr_len % 12);
 		decr_len -= (decr_len % 12);
 	}
-		
-	tmp = decr_buf + 1;
+
+	bytes += 1;
 	/* this byte seems random */
 	/*
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "Byte one of get_level packet: %d\n", buf[0]);
-	*/
+	   purple_debug(PURPLE_DEBUG_INFO, "QQ", "Byte one of get_level packet: %d\n", buf[0]);
+	   */
 	for (i = 0; i < decr_len; i += 12) {
-		uid = g_ntohl(*(guint32 *) tmp);
-		tmp += 4;
-		onlineTime = g_ntohl(*(guint32 *) tmp);
-		tmp += 4;
-		level = g_ntohs(*(guint16 *) tmp);
-		tmp += 2;
-		timeRemainder = g_ntohs(*(guint16 *) tmp);
-		tmp += 2;
-		/*
-		purple_debug(PURPLE_DEBUG_INFO, "QQ", "Level packet entry:\nuid: %d\nonlineTime: %d\nlevel: %d\ntimeRemainder: %d\n", 
+		bytes += qq_get32(&uid, decr_buf + bytes);
+		bytes += qq_get32(&onlineTime, decr_buf + bytes);
+		bytes += qq_get16(&level, decr_buf + bytes);
+		bytes += qq_get16(&timeRemainder, decr_buf + bytes);
+		purple_debug(PURPLE_DEBUG_INFO, "QQ", 
+				"Level packet entry:\nuid: %d\nonlineTime: %d\nlevel: %d\ntimeRemainder: %d\n", 
 				uid, onlineTime, level, timeRemainder);
-		*/
 		purple_name = uid_to_purple_name(uid);
 		b = purple_find_buddy(account, purple_name);
 		q_bud = (b == NULL) ? NULL : (qq_buddy *) b->proto_data;
@@ -872,7 +1026,7 @@
 			}
 		} else {
 			purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
-				"Got an online buddy %d, but not in my buddy list\n", uid);
+					"Got an online buddy %d, but not in my buddy list\n", uid);
 		}
 		g_free(purple_name);
 	}
--- a/libpurple/protocols/qq/buddy_info.h	Wed Jul 09 00:27:44 2008 +0000
+++ b/libpurple/protocols/qq/buddy_info.h	Wed Jul 09 00:32:18 2008 +0000
@@ -44,47 +44,6 @@
 #define QQ_ICON_PREFIX "qq_"
 #define QQ_ICON_SUFFIX ".png"
 
-typedef struct _contact_info {
-        gchar *uid;
-        gchar *nick;
-        gchar *country;
-        gchar *province;
-        gchar *zipcode;
-        gchar *address;
-        gchar *tel;
-        gchar *age;
-        gchar *gender;
-        gchar *name;
-        gchar *email;
-        gchar *pager_sn;
-        gchar *pager_num;
-        gchar *pager_sp;
-        gchar *pager_base_num;
-        gchar *pager_type;
-        gchar *occupation;
-        gchar *homepage;
-        gchar *auth_type;
-        gchar *unknown1;
-        gchar *unknown2;
-        gchar *face;
-        gchar *hp_num;
-        gchar *hp_type;
-        gchar *intro;
-        gchar *city;
-        gchar *unknown3;
-        gchar *unknown4;
-        gchar *unknown5;
-        gchar *is_open_hp;
-        gchar *is_open_contact;
-        gchar *college;
-        gchar *horoscope;
-        gchar *zodiac;
-        gchar *blood;
-        gchar *qq_show;
-        gchar *unknown6;        /* always 0x2D */
-} contact_info;
-
-void qq_refresh_buddy_and_myself(contact_info *info, PurpleConnection *gc);
 void qq_send_packet_get_info(PurpleConnection *gc, guint32 uid, gboolean show_window);
 void qq_set_my_buddy_icon(PurpleConnection *gc, PurpleStoredImage *img);
 void qq_set_buddy_icon_for_user(PurpleAccount *account, const gchar *who, const gchar *icon_num, const gchar *iconfile);
--- a/libpurple/protocols/qq/buddy_list.c	Wed Jul 09 00:27:44 2008 +0000
+++ b/libpurple/protocols/qq/buddy_list.c	Wed Jul 09 00:32:18 2008 +0000
@@ -38,13 +38,12 @@
 #include "crypt.h"
 #include "header_info.h"
 #include "keep_alive.h"
-#include "send_core.h"
 #include "group.h"
 #include "group_find.h"
 #include "group_internal.h"
 #include "group_info.h"
 
-#include "qq_proxy.h"
+#include "qq_network.h"
 
 #define QQ_GET_ONLINE_BUDDY_02          0x02
 #define QQ_GET_ONLINE_BUDDY_03          0x03	/* unknown function */
@@ -64,25 +63,25 @@
 void qq_send_packet_get_buddies_online(PurpleConnection *gc, guint8 position)
 {
 	qq_data *qd;
-	guint8 *raw_data, *cursor;
+	guint8 *raw_data;
+	gint bytes = 0;
 
 	qd = (qq_data *) gc->proto_data;
 	raw_data = g_newa(guint8, 5);
-	cursor = raw_data;
 
 	/* 000-000 get online friends cmd
 	 * only 0x02 and 0x03 returns info from server, other valuse all return 0xff
 	 * I can also only send the first byte (0x02, or 0x03)
 	 * and the result is the same */
-	create_packet_b(raw_data, &cursor, QQ_GET_ONLINE_BUDDY_02);
+	bytes += qq_put8(raw_data + bytes, QQ_GET_ONLINE_BUDDY_02);
 	/* 001-001 seems it supports 255 online buddies at most */
-	create_packet_b(raw_data, &cursor, position);
+	bytes += qq_put8(raw_data + bytes, position);
 	/* 002-002 */
-	create_packet_b(raw_data, &cursor, 0x00);
+	bytes += qq_put8(raw_data + bytes, 0x00);
 	/* 003-004 */
-	create_packet_w(raw_data, &cursor, 0x0000);
+	bytes += qq_put16(raw_data + bytes, 0x0000);
 
-	qq_send_cmd(gc, QQ_CMD_GET_FRIENDS_ONLINE, TRUE, 0, TRUE, raw_data, 5);
+	qq_send_cmd(qd, QQ_CMD_GET_FRIENDS_ONLINE, raw_data, 5);
 	qd->last_get_online = time(NULL);
 }
 
@@ -90,42 +89,38 @@
  * server may return a position tag if list is too long for one packet */
 void qq_send_packet_get_buddies_list(PurpleConnection *gc, guint16 position)
 {
-	guint8 *raw_data, *cursor;
-	gint data_len;
+	qq_data *qd = (qq_data *) gc->proto_data;
+	guint8 raw_data[16] = {0};
+	gint bytes = 0;
 
-	data_len = 3;
-	raw_data = g_newa(guint8, data_len);
-	cursor = raw_data;
 	/* 000-001 starting position, can manually specify */
-	create_packet_w(raw_data, &cursor, position);
+	bytes += qq_put16(raw_data + bytes, position);
 	/* before Mar 18, 2004, any value can work, and we sent 00
 	 * I do not know what data QQ server is expecting, as QQ2003iii 0304 itself
 	 * even can sending packets 00 and get no response.
 	 * Now I tested that 00,00,00,00,00,01 work perfectly
 	 * March 22, found the 00,00,00 starts to work as well */
-	create_packet_b(raw_data, &cursor, 0x00);
+	bytes += qq_put8(raw_data + bytes, 0x00);
 
-	qq_send_cmd(gc, QQ_CMD_GET_FRIENDS_LIST, TRUE, 0, TRUE, raw_data, data_len);
+	qq_send_cmd(qd, QQ_CMD_GET_FRIENDS_LIST, raw_data, bytes);
 }
 
 /* get all list, buddies & Quns with groupsid support */
 void qq_send_packet_get_all_list_with_group(PurpleConnection *gc, guint32 position)
 {
-	guint8 *raw_data, *cursor;
-	gint data_len;
+	qq_data *qd = (qq_data *) gc->proto_data;
+	guint8 raw_data[16] = {0};
+	gint bytes = 0;
 
-	data_len = 10;
-	raw_data = g_newa(guint8, data_len);
-	cursor = raw_data;
 	/* 0x01 download, 0x02, upload */
-	create_packet_b(raw_data, &cursor, 0x01);
+	bytes += qq_put8(raw_data + bytes, 0x01);
 	/* unknown 0x02 */
-	create_packet_b(raw_data, &cursor, 0x02);
+	bytes += qq_put8(raw_data + bytes, 0x02);
 	/* unknown 00 00 00 00 */
-	create_packet_dw(raw_data, &cursor, 0x00000000);
-	create_packet_dw(raw_data, &cursor, position);
+	bytes += qq_put32(raw_data + bytes, 0x00000000);
+	bytes += qq_put32(raw_data + bytes, position);
 
-	qq_send_cmd(gc, QQ_CMD_GET_ALL_LIST_WITH_GROUP, TRUE, 0, TRUE, raw_data, data_len);
+	qq_send_cmd(qd, QQ_CMD_GET_ALL_LIST_WITH_GROUP, raw_data, bytes);
 }
 
 static void _qq_buddies_online_reply_dump_unclear(qq_friends_online_entry *fe)
@@ -151,8 +146,8 @@
 void qq_process_get_buddies_online_reply(guint8 *buf, gint buf_len, PurpleConnection *gc)
 {
 	qq_data *qd;
-	gint len, bytes;
-	guint8 *data, *cursor, position;
+	gint len, bytes, bytes_buddy;
+	guint8 *data, position;
 	PurpleBuddy *b;
 	qq_buddy *q_bud;
 	qq_friends_online_entry *fe;
@@ -162,96 +157,100 @@
 	qd = (qq_data *) gc->proto_data;
 	len = buf_len;
 	data = g_newa(guint8, len);
-	cursor = data;
 
 	purple_debug(PURPLE_DEBUG_INFO, "QQ", "processing get_buddies_online_reply\n");
-	
-	if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
 
-		_qq_show_packet("Get buddies online reply packet", data, len);
-
-		read_packet_b(data, &cursor, len, &position);
-
-		fe = g_newa(qq_friends_online_entry, 1);
-		fe->s = g_newa(qq_buddy_status, 1);
+	if (!qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt buddies online");
+		return;
+	}
 
-		while (cursor < (data + len)) {
-			/* based on one online buddy entry */
-			bytes = 0;
-			/* 000-030 qq_buddy_status */
-			bytes += qq_buddy_status_read(data, &cursor, len, fe->s);
-			/* 031-032: unknown4 */
-			bytes += read_packet_w(data, &cursor, len, &fe->unknown1);
-			/* 033-033: flag1 */
-			bytes += read_packet_b(data, &cursor, len, &fe->flag1);
-			/* 034-034: comm_flag */
-			bytes += read_packet_b(data, &cursor, len, &fe->comm_flag);
-			/* 035-036: */
-			bytes += read_packet_w(data, &cursor, len, &fe->unknown2);
-			/* 037-037: */
-			bytes += read_packet_b(data, &cursor, len, &fe->ending);	/* 0x00 */
+	qq_show_packet("Get buddies online reply packet", data, len);
+
+	bytes = 0;
+	bytes += qq_get8(&position, data + bytes);
+
+	fe = g_newa(qq_friends_online_entry, 1);
+	fe->s = g_newa(qq_buddy_status, 1);
 
-			if (fe->s->uid == 0 || bytes != QQ_ONLINE_BUDDY_ENTRY_LEN) {
-				purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
-						"uid=0 or entry complete len(%d) != %d", 
-						bytes, QQ_ONLINE_BUDDY_ENTRY_LEN);
-				g_free(fe->s->ip);
-				g_free(fe->s->unknown_key);
-				continue;
-			}	/* check if it is a valid entry */
-
-			if (QQ_DEBUG)
-				_qq_buddies_online_reply_dump_unclear(fe);
+	while (bytes < len) {
+		/* set flag */
+		bytes_buddy = bytes;
+		/* based on one online buddy entry */
+		/* ATTTENTION! NEWED in the sub function, but FREED here */
+		/* 000-030 qq_buddy_status */
+		bytes += qq_buddy_status_read(fe->s, data + bytes);
+		/* 031-032: unknown4 */
+		bytes += qq_get16(&fe->unknown1, data + bytes);
+		/* 033-033: flag1 */
+		bytes += qq_get8(&fe->flag1, data + bytes);
+		/* 034-034: comm_flag */
+		bytes += qq_get8(&fe->comm_flag, data + bytes);
+		/* 035-036: */
+		bytes += qq_get16(&fe->unknown2, data + bytes);
+		/* 037-037: */
+		bytes += qq_get8(&fe->ending, data + bytes);	/* 0x00 */
 
-			/* update buddy information */
-			b = purple_find_buddy(purple_connection_get_account(gc), uid_to_purple_name(fe->s->uid));
-			q_bud = (b == NULL) ? NULL : (qq_buddy *) b->proto_data;
-
-			if (q_bud != NULL) {	/* we find one and update qq_buddy */
-				if(0 != fe->s->client_version)
-					q_bud->client_version = fe->s->client_version;
-				g_memmove(q_bud->ip, fe->s->ip, 4);
-				q_bud->port = fe->s->port;
-				q_bud->status = fe->s->status;
-				q_bud->flag1 = fe->flag1;
-				q_bud->comm_flag = fe->comm_flag;
-				qq_update_buddy_contact(gc, q_bud);
-			} else {
-				purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
-						"Got an online buddy %d, but not in my buddy list\n", fe->s->uid);
-			}
-
+		if (fe->s->uid == 0 || (bytes - bytes_buddy) != QQ_ONLINE_BUDDY_ENTRY_LEN) {
+			purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
+					"uid=0 or entry complete len(%d) != %d", 
+					(bytes - bytes_buddy), QQ_ONLINE_BUDDY_ENTRY_LEN);
 			g_free(fe->s->ip);
 			g_free(fe->s->unknown_key);
-		}
-		
-		if(cursor > (data + len)) {
-			 purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
-					"qq_process_get_buddies_online_reply: Dangerous error! maybe protocol changed, notify developers!\n");
+			continue;
+		}	/* check if it is a valid entry */
+
+		if (QQ_DEBUG) {
+			_qq_buddies_online_reply_dump_unclear(fe);
 		}
 
-		if (position != QQ_FRIENDS_ONLINE_POSITION_END) {
-			purple_debug(PURPLE_DEBUG_INFO, "QQ", "Has more online buddies, position from %d\n", position);
+		/* update buddy information */
+		b = purple_find_buddy(purple_connection_get_account(gc), uid_to_purple_name(fe->s->uid));
+		q_bud = (b == NULL) ? NULL : (qq_buddy *) b->proto_data;
 
-			qq_send_packet_get_buddies_online(gc, position);
+		if (q_bud != NULL) {	/* we find one and update qq_buddy */
+			if(0 != fe->s->client_version)
+				q_bud->client_version = fe->s->client_version;
+			g_memmove(q_bud->ip, fe->s->ip, 4);
+			q_bud->port = fe->s->port;
+			q_bud->status = fe->s->status;
+			q_bud->flag1 = fe->flag1;
+			q_bud->comm_flag = fe->comm_flag;
+			qq_update_buddy_contact(gc, q_bud);
 		} else {
-			qq_send_packet_get_buddies_levels(gc);
-			qq_refresh_all_buddy_status(gc);
+			purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
+					"Got an online buddy %d, but not in my buddy list\n", fe->s->uid);
 		}
 
+		g_free(fe->s->ip);
+		g_free(fe->s->unknown_key);
+	}
+
+	if(bytes > len) {
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
+				"qq_process_get_buddies_online_reply: Dangerous error! maybe protocol changed, notify developers!\n");
+	}
+
+	if (position != QQ_FRIENDS_ONLINE_POSITION_END) {
+		purple_debug(PURPLE_DEBUG_INFO, "QQ", "Has more online buddies, position from %d\n", position);
+
+		qq_send_packet_get_buddies_online(gc, position);
 	} else {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt buddies online");
+		qq_send_packet_get_buddies_levels(gc);
+		qq_refresh_all_buddy_status(gc);
 	}
 }
 
+
 /* process reply for get_buddies_list */
 void qq_process_get_buddies_list_reply(guint8 *buf, gint buf_len, PurpleConnection *gc)
 {
 	qq_data *qd;
 	qq_buddy *q_bud;
-	gint len, bytes, bytes_expected, i;
+	gint len, bytes_expected, i;
+	gint bytes, buddy_bytes;
 	guint16 position, unknown;
-	guint8 *data, *cursor, pascal_len;
+	guint8 *data, pascal_len;
 	gchar *name;
 	PurpleBuddy *b;
 
@@ -260,81 +259,84 @@
 	qd = (qq_data *) gc->proto_data;
 	len = buf_len;
 	data = g_newa(guint8, len);
-	cursor = data;
+
+	if (!qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt buddies list");
+		return;
+	}
+	bytes = 0;
+	bytes += qq_get16(&position, data + bytes);
+	/* the following data is buddy list in this packet */
+	i = 0;
+	while (bytes < len) {
+		q_bud = g_new0(qq_buddy, 1);
+		/* set flag */
+		buddy_bytes = bytes;
+		/* 000-003: uid */
+		bytes += qq_get32(&q_bud->uid, data + bytes);
+		/* 004-005: icon index (1-255) */
+		bytes += qq_get16(&q_bud->face, data + bytes);
+		/* 006-006: age */
+		bytes += qq_get8(&q_bud->age, data + bytes);
+		/* 007-007: gender */
+		bytes += qq_get8(&q_bud->gender, data + bytes);
 
-	if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
-		read_packet_w(data, &cursor, len, &position);
-		/* the following data is buddy list in this packet */
-		i = 0;
-		while (cursor < (data + len)) {
-			q_bud = g_new0(qq_buddy, 1);
-			bytes = 0;
-			/* 000-003: uid */
-			bytes += read_packet_dw(data, &cursor, len, &q_bud->uid);
-			/* 004-005: icon index (1-255) */
-			bytes += read_packet_w(data, &cursor, len, &q_bud->face);
-			/* 006-006: age */
-			bytes += read_packet_b(data, &cursor, len, &q_bud->age);
-			/* 007-007: gender */
-			bytes += read_packet_b(data, &cursor, len, &q_bud->gender);
-			pascal_len = convert_as_pascal_string(cursor, &q_bud->nickname, QQ_CHARSET_DEFAULT);
-			cursor += pascal_len;
-			bytes += pascal_len;
-			bytes += read_packet_w(data, &cursor, len, &unknown);
-			/* flag1: (0-7)
-			 *        bit1 => qq show
-			 * comm_flag: (0-7)
-			 *        bit1 => member
-			 *        bit4 => TCP mode
-			 *        bit5 => open mobile QQ
-			 *        bit6 => bind to mobile
-			 *        bit7 => whether having a video
-			 */
-			bytes += read_packet_b(data, &cursor, len, &q_bud->flag1);
-			bytes += read_packet_b(data, &cursor, len, &q_bud->comm_flag);
+		pascal_len = convert_as_pascal_string(data + bytes, &q_bud->nickname, QQ_CHARSET_DEFAULT);
+		bytes += pascal_len;
 
-			bytes_expected = 12 + pascal_len;
-
-			if (q_bud->uid == 0 || bytes != bytes_expected) {
-				purple_debug(PURPLE_DEBUG_INFO, "QQ",
-					   "Buddy entry, expect %d bytes, read %d bytes\n", bytes_expected, bytes);
-				g_free(q_bud->nickname);
-				g_free(q_bud);
-				continue;
-			} else {
-				i++;
-			}
+		bytes += qq_get16(&unknown, data + bytes);
+		/* flag1: (0-7)
+		 *        bit1 => qq show
+		 * comm_flag: (0-7)
+		 *        bit1 => member
+		 *        bit4 => TCP mode
+		 *        bit5 => open mobile QQ
+		 *        bit6 => bind to mobile
+		 *        bit7 => whether having a video
+		 */
+		bytes += qq_get8(&q_bud->flag1, data + bytes);
+		bytes += qq_get8(&q_bud->comm_flag, data + bytes);
 
-			if (QQ_DEBUG) {
-				purple_debug(PURPLE_DEBUG_INFO, "QQ",
-					   "buddy [%09d]: flag1=0x%02x, comm_flag=0x%02x\n",
-					   q_bud->uid, q_bud->flag1, q_bud->comm_flag);
-			}
+		bytes_expected = 12 + pascal_len;
 
-			name = uid_to_purple_name(q_bud->uid);
-			b = purple_find_buddy(gc->account, name);
-			g_free(name);
-
-			if (b == NULL)
-				b = qq_add_buddy_by_recv_packet(gc, q_bud->uid, TRUE, FALSE);
-
-			b->proto_data = q_bud;
-			qd->buddies = g_list_append(qd->buddies, q_bud);
-			qq_update_buddy_contact(gc, q_bud);
+		if (q_bud->uid == 0 || (bytes - buddy_bytes) != bytes_expected) {
+			purple_debug(PURPLE_DEBUG_INFO, "QQ",
+					"Buddy entry, expect %d bytes, read %d bytes\n", bytes_expected, bytes - buddy_bytes);
+			g_free(q_bud->nickname);
+			g_free(q_bud);
+			continue;
+		} else {
+			i++;
 		}
 
-		if(cursor > (data + len)) {
-			purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
-					"qq_process_get_buddies_list_reply: Dangerous error! maybe protocol changed, notify developers!");
-                }
-		if (position == QQ_FRIENDS_LIST_POSITION_END) {
-			purple_debug(PURPLE_DEBUG_INFO, "QQ", "Get friends list done, %d buddies\n", i);
-			qq_send_packet_get_buddies_online(gc, QQ_FRIENDS_ONLINE_POSITION_START);
-		} else {
-			qq_send_packet_get_buddies_list(gc, position);
+		if (QQ_DEBUG) {
+			purple_debug(PURPLE_DEBUG_INFO, "QQ",
+					"buddy [%09d]: flag1=0x%02x, comm_flag=0x%02x\n",
+					q_bud->uid, q_bud->flag1, q_bud->comm_flag);
+		}
+
+		name = uid_to_purple_name(q_bud->uid);
+		b = purple_find_buddy(gc->account, name);
+		g_free(name);
+
+		if (b == NULL) {
+			b = qq_add_buddy_by_recv_packet(gc, q_bud->uid, TRUE, FALSE);
 		}
+
+		b->proto_data = q_bud;
+		qd->buddies = g_list_append(qd->buddies, q_bud);
+		qq_update_buddy_contact(gc, q_bud);
+	}
+
+	if(bytes > len) {
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
+				"qq_process_get_buddies_list_reply: Dangerous error! maybe protocol changed, notify developers!");
+	}
+	if (position == QQ_FRIENDS_LIST_POSITION_END) {
+		purple_debug(PURPLE_DEBUG_INFO, "QQ", "Get friends list done, %d buddies\n", i);
+		qq_send_packet_get_buddies_online(gc, QQ_FRIENDS_ONLINE_POSITION_START);
 	} else {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt buddies list");
+		qq_send_packet_get_buddies_list(gc, position);
 	}
 }
 
@@ -342,7 +344,8 @@
 {
 	qq_data *qd;
 	gint len, i, j;
-	guint8 *data, *cursor;
+	gint bytes = 0;
+	guint8 *data;
 	guint8 sub_cmd, reply_code;
 	guint32 unknown, position;
 	guint32 uid;
@@ -354,62 +357,66 @@
 	qd = (qq_data *) gc->proto_data;
 	len = buf_len;
 	data = g_newa(guint8, len);
-	cursor = data;
+
+	if (!qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt all list with group");
+		return;
+	}
+
+	bytes += qq_get8(&sub_cmd, data + bytes);
+	g_return_if_fail(sub_cmd == 0x01);
+
+	bytes += qq_get8(&reply_code, data + bytes);
+	if(0 != reply_code) {
+		purple_debug(PURPLE_DEBUG_WARNING, "QQ", 
+				"Get all list with group reply, reply_code(%d) is not zero", reply_code);
+	}
 
-	if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
-		read_packet_b(data, &cursor, len, &sub_cmd);
-		g_return_if_fail(sub_cmd == 0x01);
-		read_packet_b(data, &cursor, len, &reply_code);
-		if(0 != reply_code) {
-			purple_debug(PURPLE_DEBUG_WARNING, "QQ", 
-					"Get all list with group reply, reply_code(%d) is not zero", reply_code);
+	bytes += qq_get32(&unknown, data + bytes);
+	bytes += qq_get32(&position, data + bytes);
+	/* the following data is all list in this packet */
+	i = 0;
+	j = 0;
+	while (bytes < len) {
+		/* 00-03: uid */
+		bytes += qq_get32(&uid, data + bytes);
+		/* 04: type 0x1:buddy 0x4:Qun */
+		bytes += qq_get8(&type, data + bytes);
+		/* 05: groupid*4 */ /* seems to always be 0 */
+		bytes += qq_get8(&groupid, data + bytes);
+		/*
+		   purple_debug(PURPLE_DEBUG_INFO, "QQ", "groupid: %i\n", groupid);
+		   groupid >>= 2;
+		   */
+		if (uid == 0 || (type != 0x1 && type != 0x4)) {
+			purple_debug(PURPLE_DEBUG_INFO, "QQ",
+					"Buddy entry, uid=%d, type=%d", uid, type);
+			continue;
+		} 
+		if(0x1 == type) { /* a buddy */
+			/* don't do anything but count - buddies are handled by 
+			 * qq_send_packet_get_buddies_list */
+			++i;
+		} else { /* a group */
+			group = qq_group_find_by_id(gc, uid, QQ_INTERNAL_ID);
+			if(group == NULL) {
+				qq_set_pending_id(&qd->adding_groups_from_server, uid, TRUE);
+				group = g_newa(qq_group, 1);
+				group->internal_group_id = uid;
+				qq_send_cmd_group_get_group_info(gc, group);
+			} else {
+				group->my_status = QQ_GROUP_MEMBER_STATUS_IS_MEMBER;
+				qq_group_refresh(gc, group);
+				qq_send_cmd_group_get_group_info(gc, group);
+			}
+			++j;
 		}
-		read_packet_dw(data, &cursor, len, &unknown);
-		read_packet_dw(data, &cursor, len, &position);
-		/* the following data is all list in this packet */
-		i = 0;
-		j = 0;
-		while (cursor < (data + len)) {
-			/* 00-03: uid */
-			read_packet_dw(data, &cursor, len, &uid);
-			/* 04: type 0x1:buddy 0x4:Qun */
-			read_packet_b(data, &cursor, len, &type);
-			/* 05: groupid*4 */ /* seems to always be 0 */
-			read_packet_b(data, &cursor, len, &groupid);
-			/*
-			purple_debug(PURPLE_DEBUG_INFO, "QQ", "groupid: %i\n", groupid);
-			groupid >>= 2;
-			*/
-			if (uid == 0 || (type != 0x1 && type != 0x4)) {
-				purple_debug(PURPLE_DEBUG_INFO, "QQ",
-					   "Buddy entry, uid=%d, type=%d", uid, type);
-				continue;
-			} 
-			if(0x1 == type) { /* a buddy */
-				/* don't do anything but count - buddies are handled by 
-				 * qq_send_packet_get_buddies_list */
-				++i;
-			} else { /* a group */
-				group = qq_group_find_by_id(gc, uid, QQ_INTERNAL_ID);
-				if(group == NULL) {
-					qq_set_pending_id(&qd->adding_groups_from_server, uid, TRUE);
-					group = g_newa(qq_group, 1);
-					group->internal_group_id = uid;
-					qq_send_cmd_group_get_group_info(gc, group);
-				} else {
-					group->my_status = QQ_GROUP_MEMBER_STATUS_IS_MEMBER;
-					qq_group_refresh(gc, group);
-					qq_send_cmd_group_get_group_info(gc, group);
-				}
-				++j;
-			}
-		}
-		if(cursor > (data + len)) {
-			 purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
-					"qq_process_get_all_list_with_group_reply: Dangerous error! maybe protocol changed, notify developers!");
-		}
-		purple_debug(PURPLE_DEBUG_INFO, "QQ", "Get all list done, %d buddies and %d Quns\n", i, j);
-	} else {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt all list with group");
 	}
+
+	if(bytes > len) {
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
+				"qq_process_get_all_list_with_group_reply: Dangerous error! maybe protocol changed, notify developers!");
+	}
+
+	purple_debug(PURPLE_DEBUG_INFO, "QQ", "Get all list done, %d buddies and %d Quns\n", i, j);
 }
--- a/libpurple/protocols/qq/buddy_opt.c	Wed Jul 09 00:27:44 2008 +0000
+++ b/libpurple/protocols/qq/buddy_opt.c	Wed Jul 09 00:32:18 2008 +0000
@@ -36,7 +36,7 @@
 #include "im.h"
 #include "keep_alive.h"
 #include "packet_parse.h"
-#include "send_core.h"
+#include "qq_network.h"
 #include "utils.h"
 
 #define PURPLE_GROUP_QQ_FORMAT          "QQ (%s)"
@@ -61,33 +61,33 @@
 /* send packet to remove a buddy from my buddy list */
 static void _qq_send_packet_remove_buddy(PurpleConnection *gc, guint32 uid)
 {
+	qq_data *qd = (qq_data *) gc->proto_data;
 	gchar uid_str[11];
 
 	g_return_if_fail(uid > 0);
 
 	g_snprintf(uid_str, sizeof(uid_str), "%d", uid);
-	qq_send_cmd(gc, QQ_CMD_DEL_FRIEND, TRUE, 0, 
-			TRUE, (guint8 *) uid_str, strlen(uid_str));
+	qq_send_cmd(qd, QQ_CMD_DEL_FRIEND, (guint8 *) uid_str, strlen(uid_str));
 }
 
 /* try to remove myself from someone's buddy list */
 static void _qq_send_packet_remove_self_from(PurpleConnection *gc, guint32 uid)
 {
-	guint8 *raw_data, *cursor;
+	qq_data *qd = (qq_data *) gc->proto_data;
+	guint8 raw_data[16] = {0};
+	gint bytes = 0;
 
 	g_return_if_fail(uid > 0);
 
-	raw_data = g_newa(guint8, 4);
-	cursor = raw_data;
-	create_packet_dw(raw_data, &cursor, uid);
+	bytes += qq_put32(raw_data + bytes, uid);
 
-	qq_send_cmd(gc, QQ_CMD_REMOVE_SELF, TRUE, 0, TRUE, raw_data, 4);
+	qq_send_cmd(qd, QQ_CMD_REMOVE_SELF, raw_data, bytes);
 }
 
 /* try to add a buddy without authentication */
 static void _qq_send_packet_add_buddy(PurpleConnection *gc, guint32 uid)
 {
-	qq_data *qd;
+	qq_data *qd = (qq_data *) gc->proto_data;
 	qq_add_buddy_request *req;
 	gchar uid_str[11];
 
@@ -95,11 +95,9 @@
 
 	/* we need to send the ascii code of this uid to qq server */
 	g_snprintf(uid_str, sizeof(uid_str), "%d", uid);
-	qq_send_cmd(gc, QQ_CMD_ADD_FRIEND_WO_AUTH, TRUE, 0, 
-			TRUE, (guint8 *) uid_str, strlen(uid_str));
+	qq_send_cmd(qd, QQ_CMD_ADD_FRIEND_WO_AUTH, (guint8 *) uid_str, strlen(uid_str));
 
 	/* must be set after sending packet to get the correct send_seq */
-	qd = (qq_data *) gc->proto_data;
 	req = g_new0(qq_add_buddy_request, 1);
 	req->seq = qd->send_seq;
 	req->uid = uid;
@@ -109,28 +107,29 @@
 /* this buddy needs authentication, text conversion is done at lowest level */
 static void _qq_send_packet_buddy_auth(PurpleConnection *gc, guint32 uid, const gchar response, const gchar *text)
 {
+	qq_data *qd = (qq_data *) gc->proto_data;
 	gchar *text_qq, uid_str[11];
-	guint8 bar, *cursor, *raw_data;
+	guint8 bar, *raw_data;
+	gint bytes = 0;
 
 	g_return_if_fail(uid != 0);
 
 	g_snprintf(uid_str, sizeof(uid_str), "%d", uid);
 	bar = 0x1f;
 	raw_data = g_newa(guint8, QQ_MSG_IM_MAX);
-	cursor = raw_data;
 
-	create_packet_data(raw_data, &cursor, (guint8 *) uid_str, strlen(uid_str));
-	create_packet_b(raw_data, &cursor, bar);
-	create_packet_b(raw_data, &cursor, response);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *) uid_str, strlen(uid_str));
+	bytes += qq_put8(raw_data + bytes, bar);
+	bytes += qq_put8(raw_data + bytes, response);
 
 	if (text != NULL) {
 		text_qq = utf8_to_qq(text, QQ_CHARSET_DEFAULT);
-		create_packet_b(raw_data, &cursor, bar);
-		create_packet_data(raw_data, &cursor, (guint8 *) text_qq, strlen(text_qq));
+		bytes += qq_put8(raw_data + bytes, bar);
+		bytes += qq_putdata(raw_data + bytes, (guint8 *) text_qq, strlen(text_qq));
 		g_free(text_qq);
 	}
 
-	qq_send_cmd(gc, QQ_CMD_BUDDY_AUTH, TRUE, 0, TRUE, raw_data, cursor - raw_data);
+	qq_send_cmd(qd, QQ_CMD_BUDDY_AUTH, raw_data, bytes);
 }
 
 static void _qq_send_packet_add_buddy_auth_with_gc_and_uid(gc_and_uid *g, const gchar *text)
@@ -210,10 +209,10 @@
 
 	nombre = uid_to_purple_name(uid);
 	purple_request_input(gc, _("Reject request"), msg1, msg2,
-			   _("Sorry, you are not my type..."), TRUE, FALSE,
-			   NULL, _("Reject"), G_CALLBACK(_qq_reject_add_request_real), _("Cancel"), NULL,
-			   purple_connection_get_account(gc), nombre, NULL,
-			   g2);
+			_("Sorry, you are not my type..."), TRUE, FALSE,
+			NULL, _("Reject"), G_CALLBACK(_qq_reject_add_request_real), _("Cancel"), NULL,
+			purple_connection_get_account(gc), nombre, NULL,
+			g2);
 	g_free(nombre);
 }
 
@@ -257,7 +256,8 @@
 {
 	qq_data *qd;
 	gint len;
-	guint8 *data, *cursor, reply;
+	gint bytes = 0;
+	guint8 *data, reply;
 	gchar **segments, *msg_utf8;
 
 	g_return_if_fail(buf != NULL && buf_len != 0);
@@ -265,22 +265,23 @@
 	qd = (qq_data *) gc->proto_data;
 	len = buf_len;
 	data = g_newa(guint8, len);
-	cursor = data;
+
+	if (!qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt add buddy with auth reply\n");
+	}
+
+	bytes += qq_get8(&reply, data + bytes);
 
-	if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
-		read_packet_b(data, &cursor, len, &reply);
-		if (reply != QQ_ADD_BUDDY_AUTH_REPLY_OK) {
-			purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Add buddy with auth request failed\n");
-			if (NULL == (segments = split_data(data, len, "\x1f", 2)))
-				return;
-			msg_utf8 = qq_to_utf8(segments[1], QQ_CHARSET_DEFAULT);
-			purple_notify_error(gc, NULL, _("Add buddy with auth request failed"), msg_utf8);
-			g_free(msg_utf8);
-		} else {
-			purple_debug(PURPLE_DEBUG_INFO, "QQ", "Add buddy with auth request OK\n");
+	if (reply != QQ_ADD_BUDDY_AUTH_REPLY_OK) {
+		purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Add buddy with auth request failed\n");
+		if (NULL == (segments = split_data(data, len, "\x1f", 2))) {
+			return;
 		}
+		msg_utf8 = qq_to_utf8(segments[1], QQ_CHARSET_DEFAULT);
+		purple_notify_error(gc, NULL, _("Add buddy with auth request failed"), msg_utf8);
+		g_free(msg_utf8);
 	} else {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt add buddy with auth reply\n");
+		purple_debug(PURPLE_DEBUG_INFO, "QQ", "Add buddy with auth request OK\n");
 	}
 }
 
@@ -289,7 +290,8 @@
 {
 	qq_data *qd;
 	gint len;
-	guint8 *data, *cursor, reply;
+	gint bytes = 0;
+	guint8 *data, reply;
 
 	g_return_if_fail(buf != NULL && buf_len != 0);
 
@@ -297,20 +299,20 @@
 	len = buf_len;
 	data = g_newa(guint8, len);
 
-	if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
-		cursor = data;
-		read_packet_b(data, &cursor, len, &reply);
-		if (reply != QQ_REMOVE_BUDDY_REPLY_OK) {
-			/* there is no reason return from server */
-			purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Remove buddy fails\n");
-		} else {		/* if reply */
-			purple_debug(PURPLE_DEBUG_INFO, "QQ", "Remove buddy OK\n");
-			/* TODO: We don't really need to notify the user about this, do we? */
-			purple_notify_info(gc, NULL, _("You have successfully removed a buddy"), NULL);
-		}
-	} else {
+	if (!qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
 		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt remove buddy reply\n");
 	}
+
+	bytes += qq_get8(&reply, data + bytes);
+
+	if (reply != QQ_REMOVE_BUDDY_REPLY_OK) {
+		/* there is no reason return from server */
+		purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Remove buddy fails\n");
+	} else {		/* if reply */
+		purple_debug(PURPLE_DEBUG_INFO, "QQ", "Remove buddy OK\n");
+		/* TODO: We don't really need to notify the user about this, do we? */
+		purple_notify_info(gc, NULL, _("You have successfully removed a buddy"), NULL);
+	}
 }
 
 /* process the server reply for my request to remove myself from a buddy */
@@ -318,7 +320,8 @@
 {
 	qq_data *qd;
 	gint len;
-	guint8 *data, *cursor, reply;
+	gint bytes = 0;
+	guint8 *data, reply;
 
 	g_return_if_fail(buf != NULL && buf_len != 0);
 
@@ -326,20 +329,20 @@
 	len = buf_len;
 	data = g_newa(guint8, len);
 
-	if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
-		cursor = data;
-		read_packet_b(data, &cursor, len, &reply);
-		if (reply != QQ_REMOVE_SELF_REPLY_OK)
-			/* there is no reason return from server */
-			purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Remove self fails\n");
-		else {		/* if reply */
-			purple_debug(PURPLE_DEBUG_INFO, "QQ", "Remove self from a buddy OK\n");
-			/* TODO: Does the user really need to be notified about this? */
-			purple_notify_info(gc, NULL, _("You have successfully removed yourself from your friend's buddy list"), NULL);
-		}
-	} else {
+	if (!qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
 		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt remove self reply\n");
 	}
+
+	bytes += qq_get8(&reply, data + bytes);
+
+	if (reply != QQ_REMOVE_SELF_REPLY_OK) {
+		/* there is no reason return from server */
+		purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Remove self fails\n");
+	} else {		/* if reply */
+		purple_debug(PURPLE_DEBUG_INFO, "QQ", "Remove self from a buddy OK\n");
+		/* TODO: Does the user really need to be notified about this? */
+		purple_notify_info(gc, NULL, _("You have successfully removed yourself from your friend's buddy list"), NULL);
+	}
 }
 
 void qq_process_add_buddy_reply(guint8 *buf, gint buf_len, guint16 seq, PurpleConnection *gc)
@@ -403,14 +406,14 @@
 			g->uid = for_uid;
 			msg = g_strdup_printf(_("User %d needs authentication"), for_uid);
 			purple_request_input(gc, NULL, msg,
-					   _("Input request here"), /* TODO: Awkward string to fix post string freeze - standardize auth dialogues? -evands */
-					   _("Would you be my friend?"),
-					   TRUE, FALSE, NULL, _("Send"),
-					   G_CALLBACK
-					   (_qq_send_packet_add_buddy_auth_with_gc_and_uid),
-					   _("Cancel"), G_CALLBACK(qq_do_nothing_with_gc_and_uid),
-					   purple_connection_get_account(gc), nombre, NULL,
-					   g);
+					_("Input request here"), /* TODO: Awkward string to fix post string freeze - standardize auth dialogues? -evands */
+					_("Would you be my friend?"),
+					TRUE, FALSE, NULL, _("Send"),
+					G_CALLBACK
+					(_qq_send_packet_add_buddy_auth_with_gc_and_uid),
+					_("Cancel"), G_CALLBACK(qq_do_nothing_with_gc_and_uid),
+					purple_connection_get_account(gc), nombre, NULL,
+					g);
 			g_free(msg);
 			g_free(nombre);
 		} else {	/* add OK */
@@ -457,7 +460,7 @@
 	g_return_val_if_fail(a != NULL && uid != 0, NULL);
 
 	group_name = is_known ?
-	    g_strdup_printf(PURPLE_GROUP_QQ_FORMAT, purple_account_get_username(a)) : g_strdup(PURPLE_GROUP_QQ_UNKNOWN);
+		g_strdup_printf(PURPLE_GROUP_QQ_FORMAT, purple_account_get_username(a)) : g_strdup(PURPLE_GROUP_QQ_UNKNOWN);
 
 	g = qq_get_purple_group(group_name);
 
@@ -512,8 +515,8 @@
 		if (b != NULL)
 			purple_blist_remove_buddy(b);
 		purple_notify_error(gc, NULL,
-				  _("QQid Error"),
-				  _("Invalid QQid"));
+				_("QQid Error"),
+				_("Invalid QQid"));
 	}
 }
 
--- a/libpurple/protocols/qq/buddy_status.c	Wed Jul 09 00:27:44 2008 +0000
+++ b/libpurple/protocols/qq/buddy_status.c	Wed Jul 09 00:32:18 2008 +0000
@@ -3,7 +3,7 @@
  *
  * purple
  *
- * Purple is the legal property of its developers, whose names are too numerous
+ * Purple is the legal property ofr its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
  * source distribution.
  *
@@ -33,10 +33,9 @@
 #include "header_info.h"
 #include "keep_alive.h"
 #include "packet_parse.h"
-#include "send_core.h"
 #include "utils.h"
 
-#include "qq_proxy.h"
+#include "qq_network.h"
 
 #define QQ_MISC_STATUS_HAVING_VIIDEO      0x00000001
 #define QQ_CHANGE_ONLINE_STATUS_REPLY_OK 	0x30	/* ASCII value of "0" */
@@ -57,7 +56,7 @@
 	g_string_append_printf(dump, "013-014:     %04x   (client_version)\n", s->client_version);
 	/* g_string_append_printf(dump, "015-030:     %s   (unknown key)\n", s->unknown_key); */
 	purple_debug(PURPLE_DEBUG_INFO, "QQ", "Buddy status entry, %s", dump->str);
-	_qq_show_packet("Unknown key", s->unknown_key, QQ_KEY_LENGTH);
+	qq_show_packet("Unknown key", s->unknown_key, QQ_KEY_LENGTH);
 	g_string_free(dump, TRUE);
 }
 
@@ -66,35 +65,33 @@
  * using different accounts to get info. */
 
 /* parse the data into qq_buddy_status */
-gint qq_buddy_status_read(guint8 *data, guint8 **cursor, gint len, qq_buddy_status *s)
+gint qq_buddy_status_read(qq_buddy_status *s, guint8 *data)
 {
-	gint bytes;
+	gint bytes = 0;
 
-	g_return_val_if_fail(data != NULL && *cursor != NULL && s != NULL, -1);
-
-	bytes = 0;
+	g_return_val_if_fail(data != NULL && s != NULL, -1);
 
 	/* 000-003: uid */
-	bytes += read_packet_dw(data, cursor, len, &s->uid);
+	bytes += qq_get32(&s->uid, data + bytes);
 	/* 004-004: 0x01 */
-	bytes += read_packet_b(data, cursor, len, &s->unknown1);
+	bytes += qq_get8(&s->unknown1, data + bytes);
 	/* this is no longer the IP, it seems QQ (as of 2006) no longer sends
 	 * the buddy's IP in this packet. all 0s */
 	/* 005-008: ip */
 	s->ip = g_new0(guint8, 4);
-	bytes += read_packet_data(data, cursor, len, s->ip, 4);
+	bytes += qq_getdata(s->ip, 4, data + bytes);
 	/* port info is no longer here either */
 	/* 009-010: port */
-	bytes += read_packet_w(data, cursor, len, &s->port);
+	bytes += qq_get16(&s->port, data + bytes);
 	/* 011-011: 0x00 */
-	bytes += read_packet_b(data, cursor, len, &s->unknown2);
+	bytes += qq_get8(&s->unknown2, data + bytes);
 	/* 012-012: status */
-	bytes += read_packet_b(data, cursor, len, &s->status);
+	bytes += qq_get8(&s->status, data + bytes);
 	/* 013-014: client_version */
-	bytes += read_packet_w(data, cursor, len, &s->client_version);
+	bytes += qq_get16(&s->client_version, data + bytes);
 	/* 015-030: unknown key */
 	s->unknown_key = g_new0(guint8, QQ_KEY_LENGTH);
-	bytes += read_packet_data(data, cursor, len, s->unknown_key, QQ_KEY_LENGTH);
+	bytes += qq_getdata(s->unknown_key, QQ_KEY_LENGTH, data + bytes);
 
 	if (s->uid == 0 || bytes != 31)
 		return -1;
@@ -106,17 +103,17 @@
 gboolean is_online(guint8 status)
 {
 	switch(status) {
-	case QQ_BUDDY_ONLINE_NORMAL:
-	case QQ_BUDDY_ONLINE_AWAY:
-	case QQ_BUDDY_ONLINE_INVISIBLE:
-		return TRUE;
-	case QQ_BUDDY_ONLINE_OFFLINE:
-		return FALSE;
+		case QQ_BUDDY_ONLINE_NORMAL:
+		case QQ_BUDDY_ONLINE_AWAY:
+		case QQ_BUDDY_ONLINE_INVISIBLE:
+			return TRUE;
+		case QQ_BUDDY_ONLINE_OFFLINE:
+			return FALSE;
 	}
 	return FALSE;
 }
 
- /* Help calculate the correct icon index to tell the server. */
+/* Help calculate the correct icon index to tell the server. */
 gint get_icon_offset(PurpleConnection *gc)
 { 
 	PurpleAccount *account;
@@ -131,7 +128,7 @@
 			|| purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_EXTENDED_AWAY)
 			|| purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_UNAVAILABLE)) {
 		return 1;
-        } else {
+	} else {
 		return 0;
 	}
 }
@@ -140,7 +137,9 @@
 void qq_send_packet_change_status(PurpleConnection *gc)
 {
 	qq_data *qd;
-	guint8 *raw_data, *cursor, away_cmd;
+	guint8 raw_data[16] = {0};
+	gint bytes = 0;
+	guint8 away_cmd;
 	guint32 misc_status;
 	gboolean fake_video;
 	PurpleAccount *account;
@@ -163,28 +162,24 @@
 		away_cmd = QQ_BUDDY_ONLINE_NORMAL;
 	}
 
-	raw_data = g_new0(guint8, 5);
-	cursor = raw_data;
 	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;
 
-	create_packet_b(raw_data, &cursor, away_cmd);
-	create_packet_dw(raw_data, &cursor, misc_status);
+	bytes = 0;
+	bytes += qq_put8(raw_data + bytes, away_cmd);
+	bytes += qq_put32(raw_data + bytes, misc_status);
 
-	qq_send_cmd(gc, QQ_CMD_CHANGE_ONLINE_STATUS, TRUE, 0, TRUE, raw_data, 5);
-
-	g_free(raw_data);
+	qq_send_cmd(qd, QQ_CMD_CHANGE_ONLINE_STATUS, raw_data, bytes);
 }
 
 /* parse the reply packet for change_status */
 void qq_process_change_status_reply(guint8 *buf, gint buf_len, PurpleConnection *gc)
 {
 	qq_data *qd;
-	gint len;
-	guint8 *data, *cursor, reply;
+	gint len, bytes;
+	guint8 *data, reply;
 	PurpleBuddy *b;
 	qq_buddy *q_bud;
 	gchar *name;
@@ -195,21 +190,22 @@
 	len = buf_len;
 	data = g_newa(guint8, len);
 
-	if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
-		cursor = data;
-		read_packet_b(data, &cursor, len, &reply);
-		if (reply != QQ_CHANGE_ONLINE_STATUS_REPLY_OK) {
-			purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Change status fail\n");
-		} else {
-			purple_debug(PURPLE_DEBUG_INFO, "QQ", "Change status OK\n");
-			name = uid_to_purple_name(qd->uid);
-			b = purple_find_buddy(gc->account, name);
-			g_free(name);
-			q_bud = (b == NULL) ? NULL : (qq_buddy *) b->proto_data;
-			qq_update_buddy_contact(gc, q_bud);
-		}
+	if ( !qq_decrypt(buf, buf_len, qd->session_key, data, &len) ) {
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt chg status reply\n");
+		return;
+	}
+
+	bytes = 0;
+	bytes = qq_get8(&reply, data + bytes);
+	if (reply != QQ_CHANGE_ONLINE_STATUS_REPLY_OK) {
+		purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Change status fail\n");
 	} else {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt chg status reply\n");
+		purple_debug(PURPLE_DEBUG_INFO, "QQ", "Change status OK\n");
+		name = uid_to_purple_name(qd->uid);
+		b = purple_find_buddy(gc->account, name);
+		g_free(name);
+		q_bud = (b == NULL) ? NULL : (qq_buddy *) b->proto_data;
+		qq_update_buddy_contact(gc, q_bud);
 	}
 }
 
@@ -219,7 +215,7 @@
 	qq_data *qd;
 	gint len, bytes;
 	guint32 my_uid;
-	guint8 *data, *cursor;
+	guint8 *data;
 	PurpleBuddy *b;
 	qq_buddy *q_bud;
 	qq_buddy_status *s;
@@ -230,51 +226,53 @@
 	qd = (qq_data *) gc->proto_data;
 	len = buf_len;
 	data = g_newa(guint8, len);
-	cursor = data;
 
-	if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
-		s = g_new0(qq_buddy_status, 1);
-		bytes = 0;
-		/* 000-030: qq_buddy_status */
-		bytes += qq_buddy_status_read(data, &cursor, len, s);
-		/* 031-034: my uid */ 
-		/* This has a value of 0 when we've changed our status to 
-		 * QQ_BUDDY_ONLINE_INVISIBLE */
-		bytes += read_packet_dw(data, &cursor, len, &my_uid);
-
-		if (bytes != 35) {
-			purple_debug(PURPLE_DEBUG_ERROR, "QQ", "bytes(%d) != 35\n", bytes);
-			g_free(s->ip);
-			g_free(s->unknown_key);
-			g_free(s);
-			return;
-		}
+	if ( !qq_decrypt(buf, buf_len, qd->session_key, data, &len) ) {
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt buddy status change packet\n");
+		return;
+	}
 
-		name = uid_to_purple_name(s->uid);
-		b = purple_find_buddy(gc->account, name);
-		g_free(name);
-		q_bud = (b == NULL) ? NULL : (qq_buddy *) b->proto_data;
-		if (q_bud) {
-			purple_debug(PURPLE_DEBUG_INFO, "QQ", "s->uid = %d, q_bud->uid = %d\n", s->uid , q_bud->uid);
-			if(0 != *((guint32 *)s->ip)) { 
-				g_memmove(q_bud->ip, s->ip, 4);
-				q_bud->port = s->port;
-			}
-			q_bud->status = s->status;
-			if(0 != s->client_version) 
-				q_bud->client_version = s->client_version; 
-			if (q_bud->status == QQ_BUDDY_ONLINE_NORMAL)
-				qq_send_packet_get_level(gc, q_bud->uid);
-			qq_update_buddy_contact(gc, q_bud);
-		} else {
-			purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
-					"got information of unknown buddy %d\n", s->uid);
-		}
+	s = g_new0(qq_buddy_status, 1);
+	bytes = 0;
+	/* 000-030: qq_buddy_status */
+	bytes += qq_buddy_status_read(s, data + bytes);
+	/* 031-034: my uid */ 
+	/* This has a value of 0 when we've changed our status to 
+	 * QQ_BUDDY_ONLINE_INVISIBLE */
+	bytes += qq_get32(&my_uid, data + bytes);
 
+	if (bytes != 35) {
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "bytes(%d) != 35\n", bytes);
 		g_free(s->ip);
 		g_free(s->unknown_key);
 		g_free(s);
+		return;
+	}
+
+	name = uid_to_purple_name(s->uid);
+	b = purple_find_buddy(gc->account, name);
+	g_free(name);
+	q_bud = (b == NULL) ? NULL : (qq_buddy *) b->proto_data;
+	if (q_bud) {
+		purple_debug(PURPLE_DEBUG_INFO, "QQ", "s->uid = %d, q_bud->uid = %d\n", s->uid , q_bud->uid);
+		if(0 != *((guint32 *)s->ip)) { 
+			g_memmove(q_bud->ip, s->ip, 4);
+			q_bud->port = s->port;
+		}
+		q_bud->status = s->status;
+		if(0 != s->client_version) {
+			q_bud->client_version = s->client_version; 
+		}
+		if (q_bud->status == QQ_BUDDY_ONLINE_NORMAL) {
+			qq_send_packet_get_level(gc, q_bud->uid);
+		}
+		qq_update_buddy_contact(gc, q_bud);
 	} else {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt buddy status change packet\n");
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
+				"got information of unknown buddy %d\n", s->uid);
 	}
+
+	g_free(s->ip);
+	g_free(s->unknown_key);
+	g_free(s);
 }
--- a/libpurple/protocols/qq/buddy_status.h	Wed Jul 09 00:27:44 2008 +0000
+++ b/libpurple/protocols/qq/buddy_status.h	Wed Jul 09 00:32:18 2008 +0000
@@ -52,7 +52,7 @@
 void qq_buddy_status_dump_unclear(qq_buddy_status *s);
 gboolean is_online(guint8 status);
 
-gint qq_buddy_status_read(guint8 *data, guint8 **cursor, gint len, qq_buddy_status *s);
+gint qq_buddy_status_read(qq_buddy_status *s, guint8 *data);
 gint get_icon_offset(PurpleConnection *gc);
 
 void qq_send_packet_change_status(PurpleConnection *gc);
--- a/libpurple/protocols/qq/char_conv.c	Wed Jul 09 00:27:44 2008 +0000
+++ b/libpurple/protocols/qq/char_conv.c	Wed Jul 09 00:32:18 2008 +0000
@@ -39,9 +39,6 @@
 #define QQ_NULL_MSG           "(NULL)"	/* return this if conversion fails */
 #define QQ_NULL_SMILEY        "(SM)"	/* return this if smiley conversion fails */
 
-/* a debug function */
-void _qq_show_packet(const gchar *desc, const guint8 *buf, gint len);
-
 const gchar qq_smiley_map[QQ_SMILEY_AMOUNT] = {
 	0x41, 0x43, 0x42, 0x44, 0x45, 0x46, 0x47, 0x48,
 	0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x73,
@@ -111,16 +108,19 @@
 
 	ret = g_convert(str, len, to_charset, from_charset, &byte_read, &byte_write, &error);
 
-	if (error == NULL)
+	if (error == NULL) {
 		return ret;	/* conversion is OK */
-	else {			/* conversion error */
-		gchar *failed = hex_dump_to_str((guint8 *) str, (len == -1) ? strlen(str) : len);
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "%s\n", error->message);
-		purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Dump failed text\n%s", failed);
-		g_free(failed);
-		g_error_free(error);
-		return g_strdup(QQ_NULL_MSG);
 	}
+	
+	/* conversion error */
+	purple_debug(PURPLE_DEBUG_ERROR, "QQ", "%s\n", error->message);
+
+	qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ",
+		(guint8 *) str, (len == -1) ? strlen(str) : len,
+		"Dump failed text");
+
+	g_error_free(error);
+	return g_strdup(QQ_NULL_MSG);
 }
 
 /* take the input as a pascal string and return a converted c-string in UTF-8
@@ -142,22 +142,23 @@
 gchar *qq_encode_to_purple(guint8 *data, gint len, const gchar *msg)
 {
 	GString *encoded;
-	guint8 font_attr, font_size, color[3], bar, *cursor;
+	guint8 font_attr, font_size, color[3], bar;
 	gboolean is_bold, is_italic, is_underline;
 	guint16 charset_code;
 	gchar *font_name, *color_code, *msg_utf8, *tmp, *ret;
+	gint bytes = 0;
 
-	cursor = data;
-	_qq_show_packet("QQ_MESG recv for font style", data, len);
+	/* checked qq_show_packet OK */
+	qq_show_packet("QQ_MESG recv for font style", data, len);
 
-	read_packet_b(data, &cursor, len, &font_attr);
-	read_packet_data(data, &cursor, len, color, 3);	/* red,green,blue */
+	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]);
 
-	read_packet_b(data, &cursor, len, &bar);	/* skip, not sure of its use */
-	read_packet_w(data, &cursor, len, &charset_code);
+	bytes += qq_get8(&bar, data + bytes);	/* skip, not sure of its use */
+	bytes += qq_get16(&charset_code, data + bytes);
 
-	tmp = g_strndup((gchar *) cursor, data + len - cursor);
+	tmp = g_strndup((gchar *)(data + bytes), len - bytes);
 	font_name = qq_to_utf8(tmp, QQ_CHARSET_DEFAULT);
 	g_free(tmp);
 
@@ -177,11 +178,11 @@
 	/* Henry: The range QQ sends rounds from 8 to 22, where a font size
 	 * of 10 is equal to 3 in html font tag */
 	g_string_append_printf(encoded,
-			       "<font color=\"%s\"><font face=\"%s\"><font size=\"%d\">",
-			       color_code, font_name, font_size / 3);
+			"<font color=\"%s\"><font face=\"%s\"><font size=\"%d\">",
+			color_code, font_name, font_size / 3);
 	purple_debug(PURPLE_DEBUG_INFO, "QQ_MESG",
-		   "recv <font color=\"%s\"><font face=\"%s\"><font size=\"%d\">\n",
-		   color_code, font_name, font_size / 3);
+			"recv <font color=\"%s\"><font face=\"%s\"><font size=\"%d\">\n",
+			color_code, font_name, font_size / 3);
 	g_string_append(encoded, msg_utf8);
 
 	if (is_bold) {
--- a/libpurple/protocols/qq/crypt.c	Wed Jul 09 00:27:44 2008 +0000
+++ b/libpurple/protocols/qq/crypt.c	Wed Jul 09 00:32:18 2008 +0000
@@ -296,3 +296,20 @@
 	}
 	return 1;
 }
+
+/* return 1 is succeed, otherwise return 0
+gint qq_crypt(gint flag,
+		const guint8 *const instr, gint instrlen, 
+		const guint8 *const key, 
+		guint8 *outstr, gint *outstrlen_ptr)
+{
+	if (flag == DECRYPT)
+		return qq_decrypt(instr, instrlen, key, outstr, outstrlen_ptr);
+	else if (flag == ENCRYPT)
+		qq_encrypt(instr, instrlen, key, outstr, outstrlen_ptr);
+	else 
+		return 0;
+
+	return 1;
+}
+*/
--- a/libpurple/protocols/qq/crypt.h	Wed Jul 09 00:27:44 2008 +0000
+++ b/libpurple/protocols/qq/crypt.h	Wed Jul 09 00:32:18 2008 +0000
@@ -35,4 +35,13 @@
 		const guint8 *const key,
 		guint8 *outstr, gint *outstrlen_ptr);
 		
+/*
+#define DECRYPT 0x00
+#define ENCRYPT 0x01
+
+gint qq_crypt(gint flag,
+	     const guint8 *const instr, gint instrlen, 
+	     const guint8 *const key, 
+	     guint8 *outstr, gint *outstrlen_ptr);
+*/
 #endif
--- a/libpurple/protocols/qq/file_trans.c	Wed Jul 09 00:27:44 2008 +0000
+++ b/libpurple/protocols/qq/file_trans.c	Wed Jul 09 00:32:18 2008 +0000
@@ -38,12 +38,11 @@
 #include "im.h"
 #include "packet_parse.h"
 #include "proxy.h"
-#include "send_core.h"
+#include "qq_network.h"
 #include "send_file.h"
 #include "utils.h"
 
 struct _qq_file_header {
-	guint8 tag;
 	guint16 client_ver;
 	guint8 file_key;
 	guint32 sender_uid;
@@ -58,11 +57,11 @@
 	key = seed | (seed << 8) | (seed << 16) | (seed << 24);
 	return key;
 }
-		
+
 static guint32 _gen_file_key(void)
 {
 	guint8 seed;
-	
+
 	seed = random();
 	return _get_file_key(seed);
 }
@@ -126,16 +125,17 @@
 	purple_cipher_context_destroy(context);
 }
 
-static void _qq_get_file_header(guint8 *buf, guint8 **cursor, gint buflen, qq_file_header *fh)
+static gint _qq_get_file_header(qq_file_header *fh, guint8 *buf)
 {
-	read_packet_b(buf, cursor, buflen, &(fh->tag));
-	read_packet_w(buf, cursor, buflen, &(fh->client_ver));
-	read_packet_b(buf, cursor, buflen, &fh->file_key);
-	read_packet_dw(buf, cursor, buflen, &(fh->sender_uid));
-	read_packet_dw(buf, cursor, buflen, &(fh->receiver_uid));
+	gint bytes = 0;
+	bytes += qq_get16(&(fh->client_ver), buf + bytes);
+	bytes += qq_get8(&fh->file_key, buf + bytes);
+	bytes += qq_get32(&(fh->sender_uid), buf + bytes);
+	bytes += qq_get32(&(fh->receiver_uid), buf + bytes);
 
 	fh->sender_uid = _decrypt_qq_uid(fh->sender_uid, _get_file_key(fh->file_key));
 	fh->receiver_uid = _decrypt_qq_uid(fh->receiver_uid, _get_file_key(fh->file_key));
+	return bytes;
 }
 
 static const gchar *qq_get_file_cmd_desc(gint type)
@@ -190,7 +190,7 @@
 		fd = open(purple_xfer_get_local_filename(xfer), O_RDWR|O_CREAT, 0644);
 		info->buffer = mmap(0, purple_xfer_get_size(xfer), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FILE, fd, 0);
 	}
-		
+
 	if (info->buffer == NULL) {
 		return - 1;
 	}
@@ -258,8 +258,8 @@
 
 static gint _qq_send_file(PurpleConnection *gc, guint8 *data, gint len, guint16 packet_type, guint32 to_uid)
 {
-	gint bytes;
-	guint8 *cursor, *buf;
+	guint8 *raw_data;
+	gint bytes = 0;
 	guint32 file_key;
 	qq_data *qd;
 	ft_info *info;
@@ -267,21 +267,19 @@
 	qd = (qq_data *) gc->proto_data;
 	g_return_val_if_fail(qd->session_key != NULL, -1);
 	info = (ft_info *) qd->xfer->data;
-	bytes = 0;
 
-	buf = g_newa(guint8, MAX_PACKET_SIZE);
-	cursor = buf;
+	raw_data = g_newa(guint8, MAX_PACKET_SIZE);
 	file_key = _gen_file_key();
 
-	bytes += create_packet_b(buf, &cursor, packet_type);
-	bytes += create_packet_w(buf, &cursor, QQ_CLIENT);
-	bytes += create_packet_b(buf, &cursor, file_key & 0xff);
-	bytes += create_packet_dw(buf, &cursor, _encrypt_qq_uid(qd->uid, file_key));
-	bytes += create_packet_dw(buf, &cursor, _encrypt_qq_uid(to_uid, file_key));
-	bytes += create_packet_data(buf, &cursor, data, len);
+	bytes += qq_put8(raw_data + bytes, packet_type);
+	bytes += qq_put16(raw_data + bytes, QQ_CLIENT);
+	bytes += qq_put8(raw_data + bytes, file_key & 0xff);
+	bytes += qq_put32(raw_data + bytes, _encrypt_qq_uid(qd->uid, file_key));
+	bytes += qq_put32(raw_data + bytes, _encrypt_qq_uid(to_uid, file_key));
+	bytes += qq_putdata(raw_data + bytes, data, len);
 
 	if (bytes == len + 12) {
-		_qq_xfer_write(buf, bytes, qd->xfer);
+		_qq_xfer_write(raw_data, bytes, qd->xfer);
 	} else
 		purple_debug(PURPLE_DEBUG_INFO, "QQ", "send_file: want %d but got %d\n", len + 12, bytes);
 	return bytes;
@@ -292,57 +290,56 @@
 {
 	qq_data *qd;
 	gint bytes, bytes_expected, encrypted_len;
-	guint8 *raw_data, *cursor, *encrypted_data;
+	guint8 *raw_data, *encrypted_data;
 	time_t now;
 	ft_info *info;
-	
+
 	qd = (qq_data *) gc->proto_data;
 	info = (ft_info *) qd->xfer->data;
 
-	raw_data = g_new0 (guint8, 61);
-	cursor = raw_data;
-	
+	raw_data = g_newa (guint8, 61);
 	bytes = 0;
+
 	now = time(NULL);
 
-	bytes += create_packet_data(raw_data, &cursor, qd->session_md5, 16);
-	bytes += create_packet_w(raw_data, &cursor, packet_type);
+	bytes += qq_putdata(raw_data + bytes, qd->session_md5, 16);
+	bytes += qq_put16(raw_data + bytes, packet_type);
 	switch (packet_type) {
 		case QQ_FILE_CMD_SENDER_SAY_HELLO:
 		case QQ_FILE_CMD_SENDER_SAY_HELLO_ACK:
 		case QQ_FILE_CMD_RECEIVER_SAY_HELLO_ACK:
 		case QQ_FILE_CMD_NOTIFY_IP_ACK:
 		case QQ_FILE_CMD_RECEIVER_SAY_HELLO:
-			bytes += create_packet_w(raw_data, &cursor, info->send_seq);
+			bytes += qq_put16(raw_data + bytes, info->send_seq);
 			break;
 		default:
-			bytes += create_packet_w(raw_data, &cursor, ++qd->send_seq);
+			bytes += qq_put16(raw_data + bytes, ++qd->send_seq);
 	}
-	bytes += create_packet_dw(raw_data, &cursor, (guint32) now);
-	bytes += create_packet_b(raw_data, &cursor, 0x00);
-	bytes += create_packet_b(raw_data, &cursor, qd->my_icon);
-	bytes += create_packet_dw(raw_data, &cursor, 0x00000000);
-	bytes += create_packet_dw(raw_data, &cursor, 0x00000000);
-	bytes += create_packet_dw(raw_data, &cursor, 0x00000000);
-	bytes += create_packet_dw(raw_data, &cursor, 0x00000000);
-	bytes += create_packet_w(raw_data, &cursor, 0x0000);
-	bytes += create_packet_b(raw_data, &cursor, 0x00);
+	bytes += qq_put32(raw_data + bytes, (guint32) now);
+	bytes += qq_put8(raw_data + bytes, 0x00);
+	bytes += qq_put8(raw_data + bytes, qd->my_icon);
+	bytes += qq_put32(raw_data + bytes, 0x00000000);
+	bytes += qq_put32(raw_data + bytes, 0x00000000);
+	bytes += qq_put32(raw_data + bytes, 0x00000000);
+	bytes += qq_put32(raw_data + bytes, 0x00000000);
+	bytes += qq_put16(raw_data + bytes, 0x0000);
+	bytes += qq_put8(raw_data + bytes, 0x00);
 	/* 0x65: send a file, 0x6b: send a custom face */
-	bytes += create_packet_b(raw_data, &cursor, QQ_FILE_TRANSFER_FILE); /* FIXME temp by gfhuang */
+	bytes += qq_put8(raw_data + bytes, QQ_FILE_TRANSFER_FILE); /* FIXME temp by gfhuang */
 	switch (packet_type)
 	{
 		case QQ_FILE_CMD_SENDER_SAY_HELLO:
 		case QQ_FILE_CMD_RECEIVER_SAY_HELLO:
 		case QQ_FILE_CMD_SENDER_SAY_HELLO_ACK:
 		case QQ_FILE_CMD_RECEIVER_SAY_HELLO_ACK:
-			bytes += create_packet_b(raw_data, &cursor, 0x00);
-			bytes += create_packet_b(raw_data, &cursor, hellobyte);
+			bytes += qq_put8(raw_data + bytes, 0x00);
+			bytes += qq_put8(raw_data + bytes, hellobyte);
 			bytes_expected = 48;
 			break;
 		case QQ_FILE_CMD_PING:
 		case QQ_FILE_CMD_PONG:
 		case QQ_FILE_CMD_NOTIFY_IP_ACK:
-			bytes += qq_fill_conn_info(raw_data, &cursor, info);
+			bytes += qq_fill_conn_info(raw_data, info);
 			bytes_expected = 61;
 			break;
 		default:
@@ -350,51 +347,53 @@
 					packet_type);
 			bytes_expected = 0;
 	}
-	
-	if (bytes == bytes_expected) {
-		gchar *hex_dump = hex_dump_to_str(raw_data, bytes);
-		purple_debug(PURPLE_DEBUG_INFO, "QQ", "sending packet[%s]: \n%s", qq_get_file_cmd_desc(packet_type), hex_dump);
-		g_free(hex_dump);
-		encrypted_len = bytes + 16;
-		encrypted_data = g_newa(guint8, encrypted_len);
-		qq_encrypt(raw_data, bytes, info->file_session_key, encrypted_data, &encrypted_len);
-		/*debug: try to decrypt it */
-		/*
-		if (QQ_DEBUG) {
-			guint8 *buf;
-			int buflen;
-			hex_dump = hex_dump_to_str(encrypted_data, encrypted_len);
-			purple_debug(PURPLE_DEBUG_INFO, "QQ", "encrypted packet: \n%s", hex_dump);
-			g_free(hex_dump);
-			buf = g_newa(guint8, MAX_PACKET_SIZE);
-			buflen = encrypted_len;
-			if (qq_decrypt(encrypted_data, encrypted_len, info->file_session_key, buf, &buflen)) {
-				purple_debug(PURPLE_DEBUG_INFO, "QQ", "decrypt success\n");
-				if (buflen == bytes && memcmp(raw_data, buf, buflen) == 0)
-					purple_debug(PURPLE_DEBUG_INFO, "QQ", "checksum ok\n");
-				hex_dump = hex_dump_to_str(buf, buflen);
-				purple_debug(PURPLE_DEBUG_INFO, "QQ", "decrypted packet: \n%s", hex_dump);
-				g_free(hex_dump);
-			} else {
-				purple_debug(PURPLE_DEBUG_INFO, "QQ", "decrypt fail\n");
-			}
-		}
-		*/
 
-		purple_debug(PURPLE_DEBUG_INFO, "QQ", "<== send %s packet\n", qq_get_file_cmd_desc(packet_type));
-		_qq_send_file(gc, encrypted_data, encrypted_len, QQ_FILE_CONTROL_PACKET_TAG, info->to_uid);
-	}
-	else
+	if (bytes != bytes_expected) {
 		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "qq_send_file_ctl_packet: Expected to get %d bytes, but get %d",
 				bytes_expected, bytes);
+		return;
+	}
+
+	qq_hex_dump(PURPLE_DEBUG_INFO, "QQ",
+		raw_data, bytes,
+		"sending packet[%s]:", qq_get_file_cmd_desc(packet_type));
+
+	encrypted_len = bytes + 16;
+	encrypted_data = g_newa(guint8, encrypted_len);
+	qq_encrypt(raw_data, bytes, info->file_session_key, encrypted_data, &encrypted_len);
+	/*debug: try to decrypt it */
+	/*
+	   if (QQ_DEBUG) {
+	   guint8 *buf;
+	   int buflen;
+	   hex_dump = hex_dump_to_str(encrypted_data, encrypted_len);
+	   purple_debug(PURPLE_DEBUG_INFO, "QQ", "encrypted packet: \n%s", hex_dump);
+	   g_free(hex_dump);
+	   buf = g_newa(guint8, MAX_PACKET_SIZE);
+	   buflen = encrypted_len;
+	   if (qq_crypt(DECRYPT, encrypted_data, encrypted_len, info->file_session_key, buf, &buflen)) {
+	   purple_debug(PURPLE_DEBUG_INFO, "QQ", "decrypt success\n");
+	   if (buflen == bytes && memcmp(raw_data, buf, buflen) == 0)
+	   purple_debug(PURPLE_DEBUG_INFO, "QQ", "checksum ok\n");
+	   hex_dump = hex_dump_to_str(buf, buflen);
+	   purple_debug(PURPLE_DEBUG_INFO, "QQ", "decrypted packet: \n%s", hex_dump);
+	   g_free(hex_dump);
+	   } else {
+	   purple_debug(PURPLE_DEBUG_INFO, "QQ", "decrypt fail\n");
+	   }
+	   }
+	   */
+
+	purple_debug(PURPLE_DEBUG_INFO, "QQ", "<== send %s packet\n", qq_get_file_cmd_desc(packet_type));
+	_qq_send_file(gc, encrypted_data, encrypted_len, QQ_FILE_CONTROL_PACKET_TAG, info->to_uid);
 }
 
 /* send a file to udp channel with QQ_FILE_DATA_PACKET_TAG */
 static void _qq_send_file_data_packet(PurpleConnection *gc, guint16 packet_type, guint8 sub_type, 
 		guint32 fragment_index, guint16 seq, guint8 *data, gint len)
 {
+	guint8 *raw_data, filename_md5[QQ_KEY_LENGTH], file_md5[QQ_KEY_LENGTH];
 	gint bytes;
-	guint8 *raw_data, *cursor, filename_md5[QQ_KEY_LENGTH], file_md5[QQ_KEY_LENGTH];
 	guint32 fragment_size = 1000;
 	gchar *filename;
 	gint filename_len, filesize;
@@ -408,17 +407,16 @@
 	filesize = purple_xfer_get_size(qd->xfer);
 
 	raw_data = g_newa(guint8, MAX_PACKET_SIZE);
-	cursor = raw_data;
 	bytes = 0;
 
-	bytes += create_packet_b(raw_data, &cursor, 0x00);
-	bytes += create_packet_w(raw_data, &cursor, packet_type);
+	bytes += qq_put8(raw_data + bytes, 0x00);
+	bytes += qq_put16(raw_data + bytes, packet_type);
 	switch (packet_type) {
 		case QQ_FILE_BASIC_INFO:
 		case QQ_FILE_DATA_INFO:
 		case QQ_FILE_EOF:
-			bytes += create_packet_w(raw_data, &cursor, 0x0000);
-			bytes += create_packet_b(raw_data, &cursor, 0x00);
+			bytes += qq_put16(raw_data + bytes, 0x0000);
+			bytes += qq_put8(raw_data + bytes, 0x00);
 			break;
 		case QQ_FILE_CMD_FILE_OP:
 			switch(sub_type)
@@ -437,44 +435,44 @@
 							"start transfering data, %d fragments with %d length each\n",
 							info->fragment_num, info->fragment_len);
 					/* Unknown */
-					bytes += create_packet_w(raw_data, &cursor, 0x0000);
+					bytes += qq_put16(raw_data  + bytes, 0x0000);
 					/* Sub-operation type */
-					bytes += create_packet_b(raw_data, &cursor, sub_type);
+					bytes += qq_put8(raw_data + bytes, sub_type);
 					/* Length of file */
-					bytes += create_packet_dw(raw_data, &cursor, filesize);
+					bytes += qq_put32(raw_data + bytes, filesize);
 					/* Number of fragments */
-					bytes += create_packet_dw(raw_data, &cursor, info->fragment_num);
+					bytes += qq_put32(raw_data + bytes, info->fragment_num);
 					/* Length of a single fragment */
-					bytes += create_packet_dw(raw_data, &cursor, info->fragment_len);
-					bytes += create_packet_data(raw_data, &cursor, file_md5, 16);
-					bytes += create_packet_data(raw_data, &cursor, filename_md5, 16);
+					bytes += qq_put32(raw_data + bytes, info->fragment_len);
+					bytes += qq_putdata(raw_data + bytes, file_md5, 16);
+					bytes += qq_putdata(raw_data + bytes, filename_md5, 16);
 					/* Length of filename */
-					bytes += create_packet_w(raw_data, &cursor, filename_len);
+					bytes += qq_put16(raw_data + bytes, filename_len);
 					/* 8 unknown bytes */
-					bytes += create_packet_dw(raw_data, &cursor, 0x00000000);
-					bytes += create_packet_dw(raw_data, &cursor, 0x00000000);
+					bytes += qq_put32(raw_data + bytes, 0x00000000);
+					bytes += qq_put32(raw_data + bytes, 0x00000000);
 					/* filename */
-					bytes += create_packet_data(raw_data, &cursor, (guint8 *) filename,
+					bytes += qq_putdata(raw_data + bytes, (guint8 *) filename,
 							filename_len);
 					break;
 				case QQ_FILE_DATA_INFO:
 					purple_debug(PURPLE_DEBUG_INFO, "QQ", 
 							"sending %dth fragment with length %d, offset %d\n",
 							fragment_index, len, (fragment_index-1)*fragment_size);
-					/* bytes += create_packet_w(raw_data, &cursor, ++(qd->send_seq)); */
-					bytes += create_packet_w(raw_data, &cursor, info->send_seq);
-					bytes += create_packet_b(raw_data, &cursor, sub_type);
-					/* bytes += create_packet_dw(raw_data, &cursor, fragment_index); */
-					bytes += create_packet_dw(raw_data, &cursor, fragment_index - 1);
-					bytes += create_packet_dw(raw_data, &cursor, (fragment_index - 1) * fragment_size);
-					bytes += create_packet_w(raw_data, &cursor, len);
-					bytes += create_packet_data(raw_data, &cursor, data, len);
+					/* bytes += qq_put16(raw_data + bytes, ++(qd->send_seq)); */
+					bytes += qq_put16(raw_data + bytes, info->send_seq);
+					bytes += qq_put8(raw_data + bytes, sub_type);
+					/* bytes += qq_put32(raw_data + bytes, fragment_index); */
+					bytes += qq_put32(raw_data + bytes, fragment_index - 1);
+					bytes += qq_put32(raw_data + bytes, (fragment_index - 1) * fragment_size);
+					bytes += qq_put16(raw_data + bytes, len);
+					bytes += qq_putdata(raw_data + bytes, data, len);
 					break;
 				case QQ_FILE_EOF:
 					purple_debug(PURPLE_DEBUG_INFO, "QQ", "end of sending data\n");
-					/* bytes += create_packet_w(raw_data, &cursor, info->fragment_num + 1); */
-					bytes += create_packet_w(raw_data, &cursor, info->fragment_num);
-					bytes += create_packet_b(raw_data, &cursor, sub_type);
+					/* bytes += qq_put16(raw_data + bytes, info->fragment_num + 1); */
+					bytes += qq_put16(raw_data + bytes, info->fragment_num);
+					bytes += qq_put8(raw_data + bytes, sub_type);
 					/* purple_xfer_set_completed(qd->xfer, TRUE); */
 			}
 			break;
@@ -482,18 +480,18 @@
 			switch (sub_type)
 			{
 				case QQ_FILE_BASIC_INFO:
-					bytes += create_packet_w(raw_data, &cursor, 0x0000);
-					bytes += create_packet_b(raw_data, &cursor, sub_type);
-					bytes += create_packet_dw(raw_data, &cursor, 0x00000000);
+					bytes += qq_put16(raw_data + bytes, 0x0000);
+					bytes += qq_put8(raw_data + bytes, sub_type);
+					bytes += qq_put32(raw_data + bytes, 0x00000000);
 					break;
 				case QQ_FILE_DATA_INFO:
-					bytes += create_packet_w(raw_data, &cursor, seq);
-					bytes += create_packet_b(raw_data, &cursor, sub_type);
-					bytes += create_packet_dw(raw_data, &cursor, fragment_index);
+					bytes += qq_put16(raw_data + bytes, seq);
+					bytes += qq_put8(raw_data + bytes, sub_type);
+					bytes += qq_put32(raw_data + bytes, fragment_index);
 					break;
 				case QQ_FILE_EOF:
-					bytes += create_packet_w(raw_data, &cursor, filesize / QQ_FILE_FRAGMENT_MAXLEN + 2);
-					bytes += create_packet_b(raw_data, &cursor, sub_type);
+					bytes += qq_put16(raw_data + bytes, filesize / QQ_FILE_FRAGMENT_MAXLEN + 2);
+					bytes += qq_put8(raw_data + bytes, sub_type);
 					break;
 			}
 	}
@@ -520,9 +518,11 @@
  */
 
 
-static void _qq_process_recv_file_ctl_packet(PurpleConnection *gc, guint8 *data, guint8 *cursor,
-		gint len, qq_file_header *fh)
+static void _qq_process_recv_file_ctl_packet(PurpleConnection *gc, guint8 *data, gint len)
 {
+	gint bytes ;
+	gint decryped_bytes;
+	qq_file_header fh;
 	guint8 *decrypted_data;
 	gint decrypted_len;
 	qq_data *qd = (qq_data *) gc->proto_data;
@@ -531,60 +531,65 @@
 	guint8 hellobyte;
 	ft_info *info = (ft_info *) qd->xfer->data;
 
+	bytes = 0;
+	bytes += _qq_get_file_header(&fh, data + bytes);
+
 	decrypted_data = g_newa(guint8, len);
 	decrypted_len = len;
 
-	if (qq_decrypt(cursor, len - (cursor - data), qd->session_md5, decrypted_data, &decrypted_len)) {
-		gchar *hex_dump;
-		cursor = decrypted_data + 16;	/* skip md5 section */
-		read_packet_w(decrypted_data, &cursor, decrypted_len, &packet_type);
-		read_packet_w(decrypted_data, &cursor, decrypted_len, &seq);
-		cursor += 4+1+1+19+1;
-		purple_debug(PURPLE_DEBUG_INFO, "QQ", "==> [%d] receive %s packet\n", seq, qq_get_file_cmd_desc(packet_type));
-		hex_dump = hex_dump_to_str(decrypted_data, decrypted_len);
-		purple_debug(PURPLE_DEBUG_INFO, "QQ", "decrypted control packet received: \n%s", hex_dump);
-		g_free(hex_dump);
-		switch (packet_type) {
-			case QQ_FILE_CMD_NOTIFY_IP_ACK:
-				cursor = decrypted_data;
-				qq_get_conn_info(decrypted_data, &cursor, decrypted_len, info);
-/*				qq_send_file_ctl_packet(gc, QQ_FILE_CMD_PING, fh->sender_uid, 0); */
-				qq_send_file_ctl_packet(gc, QQ_FILE_CMD_SENDER_SAY_HELLO, fh->sender_uid, 0);	
-				break;
-			case QQ_FILE_CMD_SENDER_SAY_HELLO:
-				/* I'm receiver, if we receive SAY_HELLO from sender, we send back the ACK */
-				cursor += 47;
-				read_packet_b(decrypted_data, &cursor, 
-						decrypted_len, &hellobyte);
+	if ( !qq_decrypt(data, len, qd->session_md5, decrypted_data, &decrypted_len) ) {
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt rcv file ctrl packet\n");
+		return;
+	}
+
+	/* only for debug info */
+	decryped_bytes = 16;	/* skip md5 section */
+	decryped_bytes += qq_get16(&packet_type, decrypted_data + decryped_bytes);
+	decryped_bytes += qq_get16(&seq, decrypted_data + decryped_bytes);
+	decryped_bytes += 4+1+1+19+1;	/* skip something */
+
+	purple_debug(PURPLE_DEBUG_INFO, "QQ", "==> [%d] receive %s packet\n", seq, qq_get_file_cmd_desc(packet_type));
+	qq_hex_dump(PURPLE_DEBUG_INFO, "QQ",
+		decrypted_data, decrypted_len,
+		"decrypted control packet received:");
 
-				qq_send_file_ctl_packet(gc, QQ_FILE_CMD_SENDER_SAY_HELLO_ACK, fh->sender_uid, hellobyte);
-				qq_send_file_ctl_packet(gc, QQ_FILE_CMD_RECEIVER_SAY_HELLO, fh->sender_uid, 0);
-				break;
-			case QQ_FILE_CMD_SENDER_SAY_HELLO_ACK:
-				/* I'm sender, do nothing */
-				break;
-			case QQ_FILE_CMD_RECEIVER_SAY_HELLO:
-				/* I'm sender, ack the hello packet and send the first data */
-				cursor += 47;
-				read_packet_b(decrypted_data, &cursor, 
-						decrypted_len, &hellobyte);
-				qq_send_file_ctl_packet(gc, QQ_FILE_CMD_RECEIVER_SAY_HELLO_ACK, fh->sender_uid, hellobyte);
-				_qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP, QQ_FILE_BASIC_INFO, 0, 0, NULL, 0);
-				break;
-			case QQ_FILE_CMD_RECEIVER_SAY_HELLO_ACK:
-				/* I'm receiver, do nothing */
-				break;
-			case QQ_FILE_CMD_PING:
-				/* I'm receiver, ack the PING */
-				qq_send_file_ctl_packet(gc, QQ_FILE_CMD_PONG, fh->sender_uid, 0);
-				break;
-			case QQ_FILE_CMD_PONG:
-				qq_send_file_ctl_packet(gc, QQ_FILE_CMD_SENDER_SAY_HELLO, fh->sender_uid, 0);
-				break;
-			default:
-				purple_debug(PURPLE_DEBUG_INFO, "QQ", "unprocess file command %d\n", packet_type);
-		}
-	} 
+	switch (packet_type) {
+		case QQ_FILE_CMD_NOTIFY_IP_ACK:
+			decryped_bytes = 0;
+			qq_get_conn_info(info, decrypted_data + decryped_bytes);
+			/* qq_send_file_ctl_packet(gc, QQ_FILE_CMD_PING, fh->sender_uid, 0); */
+			qq_send_file_ctl_packet(gc, QQ_FILE_CMD_SENDER_SAY_HELLO, fh.sender_uid, 0);	
+			break;
+		case QQ_FILE_CMD_SENDER_SAY_HELLO:
+			/* I'm receiver, if we receive SAY_HELLO from sender, we send back the ACK */
+			decryped_bytes += 47;
+			decryped_bytes += qq_get8(&hellobyte, decrypted_data + decryped_bytes);
+			qq_send_file_ctl_packet(gc, QQ_FILE_CMD_SENDER_SAY_HELLO_ACK, fh.sender_uid, hellobyte);
+			qq_send_file_ctl_packet(gc, QQ_FILE_CMD_RECEIVER_SAY_HELLO, fh.sender_uid, 0);
+			break;
+		case QQ_FILE_CMD_SENDER_SAY_HELLO_ACK:
+			/* I'm sender, do nothing */
+			break;
+		case QQ_FILE_CMD_RECEIVER_SAY_HELLO:
+			/* I'm sender, ack the hello packet and send the first data */
+			decryped_bytes += 47;
+			decryped_bytes += qq_get8(&hellobyte, decrypted_data + decryped_bytes);
+			qq_send_file_ctl_packet(gc, QQ_FILE_CMD_RECEIVER_SAY_HELLO_ACK, fh.sender_uid, hellobyte);
+			_qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP, QQ_FILE_BASIC_INFO, 0, 0, NULL, 0);
+			break;
+		case QQ_FILE_CMD_RECEIVER_SAY_HELLO_ACK:
+			/* I'm receiver, do nothing */
+			break;
+		case QQ_FILE_CMD_PING:
+			/* I'm receiver, ack the PING */
+			qq_send_file_ctl_packet(gc, QQ_FILE_CMD_PONG, fh.sender_uid, 0);
+			break;
+		case QQ_FILE_CMD_PONG:
+			qq_send_file_ctl_packet(gc, QQ_FILE_CMD_SENDER_SAY_HELLO, fh.sender_uid, 0);
+			break;
+		default:
+			purple_debug(PURPLE_DEBUG_INFO, "QQ", "unprocess file command %d\n", packet_type);
+	}
 }
 
 static void _qq_recv_file_progess(PurpleConnection *gc, guint8 *buffer, guint16 len, guint32 index, guint32 offset)
@@ -609,15 +614,15 @@
 		purple_debug(PURPLE_DEBUG_INFO, "QQ", "duplicate %dth fragment, drop it!\n", index+1);
 		return;
 	}
-		
+
 	info->window |= mask;
 
 	_qq_xfer_write_file(buffer, index, len, xfer);
-	
+
 	xfer->bytes_sent += len;
 	xfer->bytes_remaining -= len;
 	purple_xfer_update_progress(xfer);
-	
+
 	mask = 0x1 << (info->max_fragment_index % sizeof(info->window));
 	while (info->window & mask)
 	{
@@ -639,7 +644,7 @@
 	guint8 *buffer;
 	guint i;
 	gint readbytes;
-	
+
 	if (purple_xfer_get_bytes_remaining(xfer) <= 0) return;
 	if (info->window == 0 && info->max_fragment_index == 0)
 	{
@@ -655,7 +660,7 @@
 			readbytes = _qq_xfer_read_file(buffer, info->max_fragment_index + i, info->fragment_len, xfer);
 			if (readbytes > 0)
 				_qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP, QQ_FILE_DATA_INFO,
-					info->max_fragment_index + i + 1, 0, buffer, readbytes);
+						info->max_fragment_index + i + 1, 0, buffer, readbytes);
 		}
 		if (mask & 0x8000) mask = 0x0001;
 		else mask = mask << 1;
@@ -706,8 +711,8 @@
 					info->fragment_len, xfer);
 			if (readbytes > 0)
 				_qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP, QQ_FILE_DATA_INFO,
-					info->max_fragment_index + sizeof(info->window) + 1, 0, buffer, readbytes);
-			
+						info->max_fragment_index + sizeof(info->window) + 1, 0, buffer, readbytes);
+
 			info->max_fragment_index ++;
 			if (mask & 0x8000) mask = 0x0001;
 			else mask = mask << 1;
@@ -718,9 +723,10 @@
 			fragment_index, info->window, info->max_fragment_index);
 }
 
-static void _qq_process_recv_file_data(PurpleConnection *gc, guint8 *data, guint8 *cursor,
-		gint len, guint32 to_uid)
+static void _qq_process_recv_file_data(PurpleConnection *gc, guint8 *data, gint len)
 {
+	gint bytes ;
+	qq_file_header fh;
 	guint16 packet_type;
 	guint16 packet_seq;
 	guint8 sub_type;
@@ -729,24 +735,27 @@
 	guint32 fragment_offset;
 	qq_data *qd = (qq_data *) gc->proto_data;
 	ft_info *info = (ft_info *) qd->xfer->data;
-	
-	cursor += 1; /* skip an unknown byte */
-	read_packet_w(data, &cursor, len, &packet_type);
+
+	bytes = 0;
+	bytes += _qq_get_file_header(&fh, data + bytes);
+
+	bytes += 1; /* skip an unknown byte */
+	bytes += qq_get16(&packet_type, data + bytes);
 	switch(packet_type)
 	{
 		case QQ_FILE_CMD_FILE_OP:
-			read_packet_w(data, &cursor, len, &packet_seq);
-			read_packet_b(data, &cursor, len, &sub_type);
+			bytes += qq_get16(&packet_seq, data + bytes);
+			bytes += qq_get8(&sub_type, data + bytes);
 			switch (sub_type)
 			{
 				case QQ_FILE_BASIC_INFO:
-					cursor += 4;	/* file length, we have already known it from xfer */
-					read_packet_dw(data, &cursor, len, &info->fragment_num);
-					read_packet_dw(data, &cursor, len, &info->fragment_len);
+					bytes += 4;	/* file length, we have already known it from xfer */
+					bytes += qq_get32(&info->fragment_num, data + bytes);
+					bytes += qq_get32(&info->fragment_len, data + bytes);
 
-					/* FIXME: We must check the md5 here, if md5 doesn't match
-					 * we will ignore the packet or send sth as error number
-					 */
+					/* FIXME: We must check the md5 here, 
+					 * if md5 doesn't match we will ignore 
+					 * the packet or send sth as error number */
 
 					info->max_fragment_index = 0;
 					info->window = 0;
@@ -757,27 +766,27 @@
 							0, 0, NULL, 0);
 					break;
 				case QQ_FILE_DATA_INFO:
-					read_packet_dw(data, &cursor, len, &fragment_index);
-					read_packet_dw(data, &cursor, len, &fragment_offset);
-					read_packet_w(data, &cursor, len, &fragment_len);
+					bytes += qq_get32(&fragment_index, data + bytes);
+					bytes += qq_get32(&fragment_offset, data + bytes);
+					bytes += qq_get16(&fragment_len, data + bytes);
 					purple_debug(PURPLE_DEBUG_INFO, "QQ", 
 							"received %dth fragment with length %d, offset %d\n",
 							fragment_index, fragment_len, fragment_offset);
-					
+
 					_qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP_ACK, sub_type,
 							fragment_index, packet_seq, NULL, 0);
-					_qq_recv_file_progess(gc, cursor, fragment_len, fragment_index, fragment_offset);
+					_qq_recv_file_progess(gc, data + bytes, fragment_len, fragment_index, fragment_offset);
 					break;
 				case QQ_FILE_EOF:
 					purple_debug(PURPLE_DEBUG_INFO, "QQ", "end of receiving\n");
 					_qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP_ACK, sub_type,
-						0, 0, NULL, 0);
+							0, 0, NULL, 0);
 					break;
 			}
 			break;
 		case QQ_FILE_CMD_FILE_OP_ACK:
-			read_packet_w(data, &cursor, len, &packet_seq);
-			read_packet_b(data, &cursor, len, &sub_type);
+			bytes += qq_get16(&packet_seq, data + bytes);
+			bytes += qq_get8(&sub_type, data + bytes);
 			switch (sub_type)
 			{
 				case QQ_FILE_BASIC_INFO:
@@ -787,16 +796,16 @@
 					_qq_send_file_progess(gc);
 					break;
 				case QQ_FILE_DATA_INFO:
-					read_packet_dw(data, &cursor, len, &fragment_index);
+					bytes += qq_get32(&fragment_index, data + bytes);
 					_qq_update_send_progess(gc, fragment_index);
 					if (purple_xfer_is_completed(qd->xfer))
 						_qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP, QQ_FILE_EOF, 0, 0, NULL, 0);
-				/*	else
+					/*	else
 						_qq_send_file_progess(gc); */
 					break;
 				case QQ_FILE_EOF:
 					/* FIXME: OK, we can end the connection successfully */
-					
+
 					_qq_send_file_data_packet(gc, QQ_FILE_EOF, 0, 0, 0, NULL, 0);
 					purple_xfer_set_completed(qd->xfer, TRUE);
 					break;
@@ -820,21 +829,21 @@
 
 void qq_process_recv_file(PurpleConnection *gc, guint8 *data, gint len)
 {
-	guint8 *cursor;
-	qq_file_header fh;
+	gint bytes;
+	guint8 tag;
 	qq_data *qd;
 
 	qd = (qq_data *) gc->proto_data;
 
-	cursor = data;
-	_qq_get_file_header(data, &cursor, len, &fh);
+	bytes = 0;
+	bytes += qq_get8(&tag, data + bytes);
 
-	switch (fh.tag) {
+	switch (tag) {
 		case QQ_FILE_CONTROL_PACKET_TAG:
-			_qq_process_recv_file_ctl_packet(gc, data, cursor, len, &fh);
+			_qq_process_recv_file_ctl_packet(gc, data + bytes, len - bytes);
 			break;
 		case QQ_FILE_DATA_PACKET_TAG:
-			_qq_process_recv_file_data(gc, data, cursor, len, fh.sender_uid);
+			_qq_process_recv_file_data(gc, data + bytes, len - bytes);
 			break;
 		default:
 			purple_debug(PURPLE_DEBUG_INFO, "QQ", "unknown packet tag");
--- a/libpurple/protocols/qq/group_im.c	Wed Jul 09 00:27:44 2008 +0000
+++ b/libpurple/protocols/qq/group_im.c	Wed Jul 09 00:32:18 2008 +0000
@@ -58,7 +58,7 @@
 void qq_send_packet_group_im(PurpleConnection *gc, qq_group *group, const gchar *msg)
 {
 	gint data_len, bytes;
-	guint8 *raw_data, *cursor, *send_im_tail;
+	guint8 *raw_data, *send_im_tail;
 	guint16 msg_len;
 	gchar *msg_filtered;
 
@@ -67,19 +67,19 @@
 	msg_filtered = purple_markup_strip_html(msg);
 	purple_debug_info("QQ_MESG", "filterd qq qun mesg: %s\n", msg_filtered);
 	msg_len = strlen(msg_filtered);
+
 	data_len = 7 + msg_len + QQ_SEND_IM_AFTER_MSG_LEN;
 	raw_data = g_newa(guint8, data_len);
-	cursor = raw_data;
 
 	bytes = 0;
-	bytes += create_packet_b(raw_data, &cursor, QQ_GROUP_CMD_SEND_MSG);
-	bytes += create_packet_dw(raw_data, &cursor, group->internal_group_id);
-	bytes += create_packet_w(raw_data, &cursor, msg_len + QQ_SEND_IM_AFTER_MSG_LEN);
-	bytes += create_packet_data(raw_data, &cursor, (guint8 *) msg_filtered, msg_len);
+	bytes += qq_put8(raw_data + bytes, QQ_GROUP_CMD_SEND_MSG);
+	bytes += qq_put32(raw_data + bytes, group->internal_group_id);
+	bytes += qq_put16(raw_data + bytes, msg_len + QQ_SEND_IM_AFTER_MSG_LEN);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *) msg_filtered, msg_len);
 	send_im_tail = qq_get_send_im_tail(NULL, NULL, NULL,
-						   FALSE, FALSE, FALSE,
-						   QQ_SEND_IM_AFTER_MSG_LEN);
-	bytes += create_packet_data(raw_data, &cursor, send_im_tail, QQ_SEND_IM_AFTER_MSG_LEN);
+			FALSE, FALSE, FALSE,
+			QQ_SEND_IM_AFTER_MSG_LEN);
+	bytes += qq_putdata(raw_data + bytes, send_im_tail, QQ_SEND_IM_AFTER_MSG_LEN);
 	g_free(send_im_tail);
 	g_free(msg_filtered);
 
@@ -87,11 +87,11 @@
 		qq_send_group_cmd(gc, group, raw_data, data_len);
 	else
 		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
-			   "Fail creating group_im packet, expect %d bytes, build %d bytes\n", data_len, bytes);
+				"Fail creating group_im packet, expect %d bytes, build %d bytes\n", data_len, bytes);
 }
 
 /* this is the ACK */
-void qq_process_group_cmd_im(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc) 
+void qq_process_group_cmd_im(guint8 *data, gint len, PurpleConnection *gc) 
 {
 	/* return should be the internal group id
 	 * but we have nothing to do with it */
@@ -99,29 +99,26 @@
 }
 
 /* receive an application to join the group */
-void qq_process_recv_group_im_apply_join
-    (guint8 *data, guint8 **cursor, gint len, guint32 internal_group_id, PurpleConnection *gc)
+void qq_process_recv_group_im_apply_join(guint8 *data, gint len, guint32 internal_group_id, PurpleConnection *gc)
 {
 	guint32 external_group_id, user_uid;
 	guint8 group_type;
 	gchar *reason_utf8, *msg, *reason;
 	group_member_opt *g;
 	gchar *nombre;
+	gint bytes = 0;
 
 	g_return_if_fail(internal_group_id > 0 && data != NULL && len > 0);
 
-	if (*cursor >= (data + len - 1)) {
-		purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Received group msg apply_join is empty\n");
-		return;
-	}
+	/* FIXME: check length here */
 
-	read_packet_dw(data, cursor, len, &external_group_id);
-	read_packet_b(data, cursor, len, &group_type);
-	read_packet_dw(data, cursor, len, &user_uid);
+	bytes += qq_get32(&external_group_id, data + bytes);
+	bytes += qq_get8(&group_type, data + bytes);
+	bytes += qq_get32(&user_uid, data + bytes);
 
 	g_return_if_fail(external_group_id > 0 && user_uid > 0);
 
-	convert_as_pascal_string(*cursor, &reason_utf8, QQ_CHARSET_DEFAULT);
+	bytes += convert_as_pascal_string(data + bytes, &reason_utf8, QQ_CHARSET_DEFAULT);
 
 	msg = g_strdup_printf(_("User %d requested to join group %d"), user_uid, external_group_id);
 	reason = g_strdup_printf(_("Reason: %s"), reason_utf8);
@@ -134,17 +131,17 @@
 	nombre = uid_to_purple_name(user_uid);
 
 	purple_request_action(gc, _("QQ Qun Operation"),
-			    msg, reason,
-			    PURPLE_DEFAULT_ACTION_NONE,
-				purple_connection_get_account(gc), nombre, NULL,
-				g, 3,
-			    _("Approve"),
-			    G_CALLBACK
-			    (qq_group_approve_application_with_struct),
-			    _("Reject"),
-			    G_CALLBACK
-			    (qq_group_reject_application_with_struct),
-			    _("Search"), G_CALLBACK(qq_group_search_application_with_struct));
+			msg, reason,
+			PURPLE_DEFAULT_ACTION_NONE,
+			purple_connection_get_account(gc), nombre, NULL,
+			g, 3,
+			_("Approve"),
+			G_CALLBACK
+			(qq_group_approve_application_with_struct),
+			_("Reject"),
+			G_CALLBACK
+			(qq_group_reject_application_with_struct),
+			_("Search"), G_CALLBACK(qq_group_search_application_with_struct));
 
 	g_free(nombre);
 	g_free(reason);
@@ -153,31 +150,28 @@
 }
 
 /* the request to join a group is rejected */
-void qq_process_recv_group_im_been_rejected
-    (guint8 *data, guint8 **cursor, gint len, guint32 internal_group_id, PurpleConnection *gc)
+void qq_process_recv_group_im_been_rejected(guint8 *data, gint len, guint32 internal_group_id, PurpleConnection *gc)
 {
 	guint32 external_group_id, admin_uid;
 	guint8 group_type;
 	gchar *reason_utf8, *msg, *reason;
 	qq_group *group;
+	gint bytes = 0;
 
 	g_return_if_fail(data != NULL && len > 0);
 
-	if (*cursor >= (data + len - 1)) {
-		purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Received group msg been_rejected is empty\n");
-		return;
-	}
+	/* FIXME: check length here */
 
-	read_packet_dw(data, cursor, len, &external_group_id);
-	read_packet_b(data, cursor, len, &group_type);
-	read_packet_dw(data, cursor, len, &admin_uid);
+	bytes += qq_get32(&external_group_id, data + bytes);
+	bytes += qq_get8(&group_type, data + bytes);
+	bytes += qq_get32(&admin_uid, data + bytes);
 
 	g_return_if_fail(external_group_id > 0 && admin_uid > 0);
 
-	convert_as_pascal_string(*cursor, &reason_utf8, QQ_CHARSET_DEFAULT);
+	bytes += convert_as_pascal_string(data + bytes, &reason_utf8, QQ_CHARSET_DEFAULT);
 
 	msg = g_strdup_printf
-	    (_("Your request to join group %d has been rejected by admin %d"), external_group_id, admin_uid);
+		(_("Your request to join group %d has been rejected by admin %d"), external_group_id, admin_uid);
 	reason = g_strdup_printf(_("Reason: %s"), reason_utf8);
 
 	purple_notify_warning(gc, _("QQ Qun Operation"), msg, reason);
@@ -194,31 +188,28 @@
 }
 
 /* the request to join a group is approved */
-void qq_process_recv_group_im_been_approved
-    (guint8 *data, guint8 **cursor, gint len, guint32 internal_group_id, PurpleConnection *gc)
+void qq_process_recv_group_im_been_approved(guint8 *data, gint len, guint32 internal_group_id, PurpleConnection *gc)
 {
 	guint32 external_group_id, admin_uid;
 	guint8 group_type;
 	gchar *reason_utf8, *msg;
 	qq_group *group;
+	gint bytes = 0;
 
 	g_return_if_fail(data != NULL && len > 0);
 
-	if (*cursor >= (data + len - 1)) {
-		purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Received group msg been_approved is empty\n");
-		return;
-	}
+	/* FIXME: check length here */
 
-	read_packet_dw(data, cursor, len, &external_group_id);
-	read_packet_b(data, cursor, len, &group_type);
-	read_packet_dw(data, cursor, len, &admin_uid);
+	bytes += qq_get32(&external_group_id, data + bytes);
+	bytes += qq_get8(&group_type, data + bytes);
+	bytes += qq_get32(&admin_uid, data + bytes);
 
 	g_return_if_fail(external_group_id > 0 && admin_uid > 0);
 	/* it is also a "无" here, so do not display */
-	convert_as_pascal_string(*cursor, &reason_utf8, QQ_CHARSET_DEFAULT);
+	bytes += convert_as_pascal_string(data + bytes, &reason_utf8, QQ_CHARSET_DEFAULT);
 
 	msg = g_strdup_printf
-	    (_("Your request to join group %d has been approved by admin %d"), external_group_id, admin_uid);
+		(_("Your request to join group %d has been approved by admin %d"), external_group_id, admin_uid);
 
 	purple_notify_warning(gc, _("QQ Qun Operation"), msg, NULL);
 
@@ -233,24 +224,21 @@
 }
 
 /* process the packet when removed from a group */
-void qq_process_recv_group_im_been_removed
-    (guint8 *data, guint8 **cursor, gint len, guint32 internal_group_id, PurpleConnection *gc)
+void qq_process_recv_group_im_been_removed(guint8 *data, gint len, guint32 internal_group_id, PurpleConnection *gc)
 {
 	guint32 external_group_id, uid;
 	guint8 group_type;
 	gchar *msg;
 	qq_group *group;
+	gint bytes = 0;
 
 	g_return_if_fail(data != NULL && len > 0);
 
-	if (*cursor >= (data + len - 1)) {
-		purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Received group msg been_removed is empty\n");
-		return;
-	}
+	/* FIXME: check length here */
 
-	read_packet_dw(data, cursor, len, &external_group_id);
-	read_packet_b(data, cursor, len, &group_type);
-	read_packet_dw(data, cursor, len, &uid);
+	bytes += qq_get32(&external_group_id, data + bytes);
+	bytes += qq_get8(&group_type, data + bytes);
+	bytes += qq_get32(&uid, data + bytes);
 
 	g_return_if_fail(external_group_id > 0 && uid > 0);
 
@@ -267,24 +255,21 @@
 }
 
 /* process the packet when added to a group */
-void qq_process_recv_group_im_been_added
-    (guint8 *data, guint8 **cursor, gint len, guint32 internal_group_id, PurpleConnection *gc)
+void qq_process_recv_group_im_been_added(guint8 *data, gint len, guint32 internal_group_id, PurpleConnection *gc)
 {
 	guint32 external_group_id, uid;
 	guint8 group_type;
 	qq_group *group;
 	gchar *msg;
+	gint bytes = 0;
 
 	g_return_if_fail(data != NULL && len > 0);
 
-	if (*cursor >= (data + len - 1)) {
-		purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Received group msg been_added is empty\n");
-		return;
-	}
+	/* FIXME: check length here */
 
-	read_packet_dw(data, cursor, len, &external_group_id);
-	read_packet_b(data, cursor, len, &group_type);
-	read_packet_dw(data, cursor, len, &uid);
+	bytes += qq_get32(&external_group_id, data + bytes);
+	bytes += qq_get8(&group_type, data + bytes);
+	bytes += qq_get32(&uid, data + bytes);
 
 	g_return_if_fail(external_group_id > 0 && uid > 0);
 
@@ -307,10 +292,9 @@
 }
 
 /* recv an IM from a group chat */
-void qq_process_recv_group_im(guint8 *data, guint8 **cursor, gint data_len, 
-		guint32 internal_group_id, PurpleConnection *gc, guint16 im_type)
+void qq_process_recv_group_im(guint8 *data, gint data_len, guint32 internal_group_id, PurpleConnection *gc, guint16 im_type)
 {
-	gchar *msg_with_purple_smiley, *msg_utf8_encoded, *im_src_name, *hex_dump;
+	gchar *msg_with_purple_smiley, *msg_utf8_encoded, *im_src_name;
 	guint16 unknown;
 	guint32 unknown4;
 	PurpleConversation *conv;
@@ -319,32 +303,32 @@
 	qq_group *group;
 	qq_recv_group_im *im_group;
 	gint skip_len;
+	gint bytes = 0;
 
 	g_return_if_fail(data != NULL && data_len > 0);
+
+	/* FIXME: check length here */
+
 	qd = (qq_data *) gc->proto_data;
 
-	hex_dump = hex_dump_to_str(*cursor, data_len - (*cursor - data));
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "group im hex dump\n%s\n", hex_dump);
-
-	if (*cursor >= (data + data_len - 1)) {
-		purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Received group im_group is empty\n");
-		return;
-	}
+	qq_hex_dump(PURPLE_DEBUG_INFO, "QQ",
+		data, data_len,
+		"group im hex dump");
 
 	im_group = g_newa(qq_recv_group_im, 1);
 
-	read_packet_dw(data, cursor, data_len, &(im_group->external_group_id));
-	read_packet_b(data, cursor, data_len, &(im_group->group_type));
+	bytes += qq_get32(&(im_group->external_group_id), data + bytes);
+	bytes += qq_get8(&(im_group->group_type), data + bytes);
 
 	if(QQ_RECV_IM_TEMP_QUN_IM == im_type) {
-		read_packet_dw(data, cursor, data_len, &(internal_group_id));
+		bytes += qq_get32(&(internal_group_id), data + bytes);
 	}
 
-	read_packet_dw(data, cursor, data_len, &(im_group->member_uid));
-	read_packet_w(data, cursor, data_len, &unknown);	/* 0x0001? */
-	read_packet_w(data, cursor, data_len, &(im_group->msg_seq));
-	read_packet_time(data, cursor, data_len, &im_group->send_time);
-	read_packet_dw(data, cursor, data_len, &unknown4);	/* versionID */
+	bytes += qq_get32(&(im_group->member_uid), bytes + data);
+	bytes += qq_get16(&unknown, data + bytes);	/* 0x0001? */
+	bytes += qq_get16(&(im_group->msg_seq), data + bytes);
+	bytes += qq_getime(&im_group->send_time, data + bytes);
+	bytes += qq_get32(&unknown4, data + bytes);	/* versionID */
 	/*
 	 * length includes font_attr
 	 * this msg_len includes msg and font_attr
@@ -355,7 +339,7 @@
 	 * 3. font_attr
 	 */
 
-	read_packet_w(data, cursor, data_len, &(im_group->msg_len));
+	bytes += qq_get16(&(im_group->msg_len), data + bytes);
 	g_return_if_fail(im_group->msg_len > 0);
 
 	/*
@@ -371,14 +355,14 @@
 		skip_len = 10;
 	else
 		skip_len = 0;
-	*cursor += skip_len;
+	bytes += skip_len;
 
-	im_group->msg = g_strdup((gchar *) *cursor);
-	*cursor += strlen(im_group->msg) + 1;
+	im_group->msg = g_strdup((gchar *) data + bytes);
+	bytes += strlen(im_group->msg) + 1;
 	/* there might not be any font_attr, check it */
 	im_group->font_attr_len = im_group->msg_len - strlen(im_group->msg) - 1 - skip_len;
 	if (im_group->font_attr_len > 0)
-		im_group->font_attr = g_memdup(*cursor, im_group->font_attr_len);
+		im_group->font_attr = g_memdup(data + bytes, im_group->font_attr_len);
 	else
 		im_group->font_attr = NULL;
 
@@ -386,7 +370,7 @@
 	msg_with_purple_smiley = qq_smiley_to_purple(im_group->msg);
 	if (im_group->font_attr_len > 0)
 		msg_utf8_encoded = qq_encode_to_purple(im_group->font_attr,
-						     im_group->font_attr_len, msg_with_purple_smiley);
+				im_group->font_attr_len, msg_with_purple_smiley);
 	else
 		msg_utf8_encoded = qq_to_utf8(msg_with_purple_smiley, QQ_CHARSET_DEFAULT);
 
@@ -406,11 +390,10 @@
 		else
 			im_src_name = g_strdup(member->nickname);
 		serv_got_chat_in(gc,
-				 purple_conv_chat_get_id(PURPLE_CONV_CHAT
-						       (conv)), im_src_name, 0, msg_utf8_encoded, im_group->send_time);
+				purple_conv_chat_get_id(PURPLE_CONV_CHAT
+					(conv)), im_src_name, 0, msg_utf8_encoded, im_group->send_time);
 		g_free(im_src_name);
 	}
-	g_free(hex_dump);
 	g_free(msg_with_purple_smiley);
 	g_free(msg_utf8_encoded);
 	g_free(im_group->msg);
--- a/libpurple/protocols/qq/group_im.h	Wed Jul 09 00:27:44 2008 +0000
+++ b/libpurple/protocols/qq/group_im.h	Wed Jul 09 00:32:18 2008 +0000
@@ -30,17 +30,31 @@
 #include "group.h"
 
 void qq_send_packet_group_im(PurpleConnection *gc, qq_group *group, const gchar *msg);
-void qq_process_group_cmd_im(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc);
-void qq_process_recv_group_im(guint8 *data, 
-		guint8 **cursor, gint data_len, guint32 internal_group_id, PurpleConnection *gc, guint16 im_type);
-void qq_process_recv_group_im_apply_join(guint8 *data,
-				    guint8 **cursor, gint len, guint32 internal_group_id, PurpleConnection *gc);
-void qq_process_recv_group_im_been_rejected(guint8 *data,
-				       guint8 **cursor, gint len, guint32 internal_group_id, PurpleConnection *gc);
-void qq_process_recv_group_im_been_approved(guint8 *data,
-				       guint8 **cursor, gint len, guint32 internal_group_id, PurpleConnection *gc);
-void qq_process_recv_group_im_been_removed(guint8 *data,
-				      guint8 **cursor, gint len, guint32 internal_group_id, PurpleConnection *gc);
-void qq_process_recv_group_im_been_added(guint8 *data,
-				    guint8 **cursor, gint len, guint32 internal_group_id, PurpleConnection *gc);
+
+/* void qq_process_group_cmd_im(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc); */
+void qq_process_group_cmd_im(guint8 *data, gint len, PurpleConnection *gc);
+
+/* void qq_process_recv_group_im(guint8 *data, guint8 **cursor, 
+ * gint data_len, guint32 internal_group_id, PurpleConnection *gc, guint16 im_type); */
+void qq_process_recv_group_im(guint8 *data, gint data_len, guint32 internal_group_id, PurpleConnection *gc, guint16 im_type);
+
+/* void qq_process_recv_group_im_apply_join(guint8 *data, guint8 **cursor, gint len, 
+ * guint32 internal_group_id, PurpleConnection *gc); */
+void qq_process_recv_group_im_apply_join(guint8 *data, gint len, guint32 internal_group_id, PurpleConnection *gc);
+
+/* void qq_process_recv_group_im_been_rejected(guint8 *data, guint8 **cursor, gint len, 
+ * guint32 internal_group_id, PurpleConnection *gc); */
+void qq_process_recv_group_im_been_rejected(guint8 *data, gint len, guint32 internal_group_id, PurpleConnection *gc);
+
+/* void qq_process_recv_group_im_been_approved(guint8 *data, guint8 **cursor, gint len, 
+ * guint32 internal_group_id, PurpleConnection *gc); */
+void qq_process_recv_group_im_been_approved(guint8 *data, gint len, guint32 internal_group_id, PurpleConnection *gc);
+
+/* void qq_process_recv_group_im_been_removed(guint8 *data, guint8 **cursor, gint len, 
+ * guint32 internal_group_id, PurpleConnection *gc); */
+void qq_process_recv_group_im_been_removed(guint8 *data, gint len, guint32 internal_group_id, PurpleConnection *gc);
+
+/* void qq_process_recv_group_im_been_added(guint8 *data,  guint8 **cursor, gint len, 
+ * guint32 internal_group_id, PurpleConnection *gc); */
+void qq_process_recv_group_im_been_added(guint8 *data, gint len, guint32 internal_group_id, PurpleConnection *gc);
 #endif
--- a/libpurple/protocols/qq/group_info.c	Wed Jul 09 00:27:44 2008 +0000
+++ b/libpurple/protocols/qq/group_info.c	Wed Jul 09 00:32:18 2008 +0000
@@ -43,7 +43,7 @@
 {
 	g_return_val_if_fail(member != NULL, FALSE);
 	return (member->nickname == NULL) ||
-	    (time(NULL) - member->last_refresh) > QQ_GROUP_CHAT_REFRESH_NICKNAME_INTERNAL;
+		(time(NULL) - member->last_refresh) > QQ_GROUP_CHAT_REFRESH_NICKNAME_INTERNAL;
 }
 
 /* this is done when we receive the reply to get_online_members sub_cmd
@@ -65,100 +65,83 @@
 /* send packet to get detailed information of one group */
 void qq_send_cmd_group_get_group_info(PurpleConnection *gc, qq_group *group)
 {
-	guint8 *raw_data, *cursor;
-	gint bytes, data_len;
+	guint8 raw_data[16] = {0};
+	gint bytes = 0;
 
 	g_return_if_fail(group != NULL);
 
-	data_len = 5;
-	raw_data = g_newa(guint8, data_len);
-	cursor = raw_data;
+	bytes += qq_put8(raw_data + bytes, QQ_GROUP_CMD_GET_GROUP_INFO);
+	bytes += qq_put32(raw_data + bytes, group->internal_group_id);
 
-	bytes = 0;
-	bytes += create_packet_b(raw_data, &cursor, QQ_GROUP_CMD_GET_GROUP_INFO);
-	bytes += create_packet_dw(raw_data, &cursor, group->internal_group_id);
-
-	if (bytes != data_len)
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
-			   "Fail create packet for %s\n", qq_group_cmd_get_desc(QQ_GROUP_CMD_GET_GROUP_INFO));
-	else
-		qq_send_group_cmd(gc, group, raw_data, data_len);
+	qq_send_group_cmd(gc, group, raw_data, bytes);
 }
 
 /* send packet to get online group member, called by keep_alive */
 void qq_send_cmd_group_get_online_members(PurpleConnection *gc, qq_group *group)
 {
-	guint8 *raw_data, *cursor;
-	gint bytes, data_len;
+	guint8 raw_data[16] = {0};
+	gint bytes = 0;
 
 	g_return_if_fail(group != NULL);
 
 	/* only get online members when conversation window is on */
 	if (NULL == purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,group->group_name_utf8, purple_connection_get_account(gc))) {
 		purple_debug(PURPLE_DEBUG_WARNING, "QQ",
-			   "Conv windows for \"%s\" is not on, do not get online members\n", group->group_name_utf8);
+				"Conv windows for \"%s\" is not on, do not get online members\n", group->group_name_utf8);
 		return;
 	}
 
-	data_len = 5;
-	raw_data = g_newa(guint8, data_len);
-	cursor = raw_data;
+	bytes += qq_put8(raw_data + bytes, QQ_GROUP_CMD_GET_ONLINE_MEMBER);
+	bytes += qq_put32(raw_data + bytes, group->internal_group_id);
 
-	bytes = 0;
-	bytes += create_packet_b(raw_data, &cursor, QQ_GROUP_CMD_GET_ONLINE_MEMBER);
-	bytes += create_packet_dw(raw_data, &cursor, group->internal_group_id);
-
-	if (bytes != data_len)
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
-			   "Fail create packet for %s\n", qq_group_cmd_get_desc(QQ_GROUP_CMD_GET_ONLINE_MEMBER));
-	else
-		qq_send_group_cmd(gc, group, raw_data, data_len);
+	qq_send_group_cmd(gc, group, raw_data, bytes);
 }
 
 /* send packet to get info for each group member */
 void qq_send_cmd_group_get_members_info(PurpleConnection *gc, qq_group *group)
 {
-	guint8 *raw_data, *cursor;
-	gint bytes, data_len, i;
+	guint8 *raw_data;
+	gint bytes, num, data_len;
 	GList *list;
 	qq_buddy *member;
 
 	g_return_if_fail(group != NULL);
-	for (i = 0, list = group->members; list != NULL; list = list->next) {
+	for (num = 0, list = group->members; list != NULL; list = list->next) {
 		member = (qq_buddy *) list->data;
 		if (_is_group_member_need_update_info(member))
-			i++;
+			num++;
 	}
 
-	if (i <= 0) {
+	if (num <= 0) {
 		purple_debug(PURPLE_DEBUG_INFO, "QQ", "No group member needs to to update info now.\n");
 		return;
 	}
 
-	data_len = 5 + 4 * i;
+	data_len = 5 + 4 * num;
 	raw_data = g_newa(guint8, data_len);
-	cursor = raw_data;
 
 	bytes = 0;
-	bytes += create_packet_b(raw_data, &cursor, QQ_GROUP_CMD_GET_MEMBER_INFO);
-	bytes += create_packet_dw(raw_data, &cursor, group->internal_group_id);
+	bytes += qq_put8(raw_data + bytes, QQ_GROUP_CMD_GET_MEMBER_INFO);
+	bytes += qq_put32(raw_data + bytes, group->internal_group_id);
 
 	list = group->members;
 	while (list != NULL) {
 		member = (qq_buddy *) list->data;
 		if (_is_group_member_need_update_info(member))
-			bytes += create_packet_dw(raw_data, &cursor, member->uid);
+			bytes += qq_put32(raw_data + bytes, member->uid);
 		list = list->next;
 	}
 
-	if (bytes != data_len)
+	if (bytes != data_len) {
 		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
-			   "Fail create packet for %s\n", qq_group_cmd_get_desc(QQ_GROUP_CMD_GET_MEMBER_INFO));
-	else
-		qq_send_group_cmd(gc, group, raw_data, data_len);
+				"Fail create packet for %s\n", qq_group_cmd_get_desc(QQ_GROUP_CMD_GET_MEMBER_INFO));
+		return;
+	}
+
+	qq_send_group_cmd(gc, group, raw_data, bytes);
 }
 
-void qq_process_group_cmd_get_group_info(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc)
+void qq_process_group_cmd_get_group_info(guint8 *data, gint len, PurpleConnection *gc)
 {
 	qq_group *group;
 	qq_buddy *member;
@@ -168,16 +151,18 @@
 	guint16 unknown, max_members;
 	guint32 member_uid, internal_group_id, external_group_id;
 	GSList *pending_id;
-	gint pascal_len, i;
 	guint32 unknown4;
 	guint8 unknown1;
+	gint bytes, num;
 
 	g_return_if_fail(data != NULL && len > 0);
 	qd = (qq_data *) gc->proto_data;
 
-	read_packet_dw(data, cursor, len, &(internal_group_id));
+	bytes = 0;
+	bytes += qq_get32(&(internal_group_id), data + bytes);
 	g_return_if_fail(internal_group_id > 0);
-	read_packet_dw(data, cursor, len, &(external_group_id));
+
+	bytes += qq_get32(&(external_group_id), data + bytes);
 	g_return_if_fail(internal_group_id > 0);
 
 	pending_id = qq_get_pending_id(qd->adding_groups_from_server, internal_group_id);
@@ -189,32 +174,30 @@
 	group = qq_group_find_by_id(gc, internal_group_id, QQ_INTERNAL_ID);
 	g_return_if_fail(group != NULL);
 
-	read_packet_b(data, cursor, len, &(group->group_type));
-	read_packet_dw(data, cursor, len, &unknown4);	/* unknown 4 bytes */
-	read_packet_dw(data, cursor, len, &(group->creator_uid));
-	read_packet_b(data, cursor, len, &(group->auth_type));
-	read_packet_dw(data, cursor, len, &unknown4);	/* oldCategory */
-	read_packet_w(data, cursor, len, &unknown);	
-	read_packet_dw(data, cursor, len, &(group->group_category));
-	read_packet_w(data, cursor, len, &max_members);
-	read_packet_b(data, cursor, len, &unknown1);
-	read_packet_dw(data, cursor, len, &(unknown4));	/* versionID */
+	bytes += qq_get8(&(group->group_type), data + bytes);
+	bytes += qq_get32(&unknown4, data + bytes);	/* unknown 4 bytes */
+	bytes += qq_get32(&(group->creator_uid), data + bytes);
+	bytes += qq_get8(&(group->auth_type), data + bytes);
+	bytes += qq_get32(&unknown4, data + bytes);	/* oldCategory */
+	bytes += qq_get16(&unknown, data + bytes);	
+	bytes += qq_get32(&(group->group_category), data + bytes);
+	bytes += qq_get16(&max_members, data + bytes);
+	bytes += qq_get8(&unknown1, data + bytes);
+	bytes += qq_get32(&(unknown4), data + bytes);	/* versionID */
 
-	pascal_len = convert_as_pascal_string(*cursor, &(group->group_name_utf8), QQ_CHARSET_DEFAULT);
-	*cursor += pascal_len;
-	read_packet_w(data, cursor, len, &(unknown));	/* 0x0000 */
-	pascal_len = convert_as_pascal_string(*cursor, &(group->notice_utf8), QQ_CHARSET_DEFAULT);
-	*cursor += pascal_len;
-	pascal_len = convert_as_pascal_string(*cursor, &(group->group_desc_utf8), QQ_CHARSET_DEFAULT);
-	*cursor += pascal_len;
+	/* strlen + <str content> */
+	bytes += convert_as_pascal_string(data + bytes, &(group->group_name_utf8), QQ_CHARSET_DEFAULT);
+	bytes += qq_get16(&unknown, data + bytes);	/* 0x0000 */
+	bytes += convert_as_pascal_string(data + bytes, &(group->notice_utf8), QQ_CHARSET_DEFAULT);
+	bytes += convert_as_pascal_string(data + bytes, &(group->group_desc_utf8), QQ_CHARSET_DEFAULT);
 
-	i = 0;
+	num = 0;
 	/* now comes the member list separated by 0x00 */
-	while (*cursor < data + len) {
-		read_packet_dw(data, cursor, len, &member_uid);
-		i++;
-		read_packet_b(data, cursor, len, &organization);
-		read_packet_b(data, cursor, len, &role);
+	while (bytes < len) {
+		bytes += qq_get32(&member_uid, data + bytes);
+		num++;
+		bytes += qq_get8(&organization, data + bytes);
+		bytes += qq_get8(&role, data + bytes);
 
 		if(organization != 0 || role != 0) {
 			purple_debug(PURPLE_DEBUG_INFO, "QQ", "group member %d: organization=%d, role=%d\n", member_uid, organization, role);
@@ -223,11 +206,11 @@
 		if (member != NULL)
 			member->role = role;
 	}
-        if(*cursor > (data + len)) {
-                         purple_debug(PURPLE_DEBUG_ERROR, "QQ", "group_cmd_get_group_info: Dangerous error! maybe protocol changed, notify me!");
-        }
+	if(bytes > len) {
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "group_cmd_get_group_info: Dangerous error! maybe protocol changed, notify me!");
+	}
 
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "group \"%s\" has %d members\n", group->group_name_utf8, i);
+	purple_debug(PURPLE_DEBUG_INFO, "QQ", "group \"%s\" has %d members\n", group->group_name_utf8, num);
 
 	if (group->creator_uid == qd->uid)
 		group->my_status = QQ_GROUP_MEMBER_STATUS_IS_ADMIN;
@@ -237,33 +220,32 @@
 	purple_conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, 
 			group->group_name_utf8, purple_connection_get_account(gc));
 	if(NULL == purple_conv) {
-                purple_debug(PURPLE_DEBUG_WARNING, "QQ",
-                           "Conv windows for \"%s\" is not on, do not set topic\n", group->group_name_utf8);
+		purple_debug(PURPLE_DEBUG_WARNING, "QQ",
+				"Conv windows for \"%s\" is not on, do not set topic\n", group->group_name_utf8);
 	}
 	else {
 		purple_conv_chat_set_topic(PURPLE_CONV_CHAT(purple_conv), NULL, group->notice_utf8);
 	}
 }
 
-void qq_process_group_cmd_get_online_members(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc)
+void qq_process_group_cmd_get_online_members(guint8 *data, gint len, PurpleConnection *gc)
 {
 	guint32 internal_group_id, member_uid;
 	guint8 unknown;
-	gint bytes, i;
+	gint bytes, num;
 	qq_group *group;
 	qq_buddy *member;
 
 	g_return_if_fail(data != NULL && len > 0);
 
-	if (data + len - *cursor < 4) {
+	if (len <= 3) {
 		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Invalid group online member reply, discard it!\n");
 		return;
 	}
 
 	bytes = 0;
-	i = 0;
-	bytes += read_packet_dw(data, cursor, len, &internal_group_id);
-	bytes += read_packet_b(data, cursor, len, &unknown);	/* 0x3c ?? */
+	bytes += qq_get32(&internal_group_id, data + bytes);
+	bytes += qq_get8(&unknown, data + bytes);	/* 0x3c ?? */
 	g_return_if_fail(internal_group_id > 0);
 
 	group = qq_group_find_by_id(gc, internal_group_id, QQ_INTERNAL_ID);
@@ -275,61 +257,63 @@
 
 	/* set all offline first, then update those online */
 	_qq_group_set_members_all_offline(group);
-	while (*cursor < data + len) {
-		bytes += read_packet_dw(data, cursor, len, &member_uid);
-		i++;
+	num = 0;
+	while (bytes < len) {
+		bytes += qq_get32(&member_uid, data + bytes);
+		num++;
 		member = qq_group_find_or_add_member(gc, group, member_uid);
 		if (member != NULL)
 			member->status = QQ_BUDDY_ONLINE_NORMAL;
 	}
-        if(*cursor > (data + len)) {
-                         purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
-					 "group_cmd_get_online_members: Dangerous error! maybe protocol changed, notify developers!");
-        }
+	if(bytes > len) {
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
+				"group_cmd_get_online_members: Dangerous error! maybe protocol changed, notify developers!");
+	}
 
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "Group \"%s\" has %d online members\n", group->group_name_utf8, i);
+	purple_debug(PURPLE_DEBUG_INFO, "QQ", "Group \"%s\" has %d online members\n", group->group_name_utf8, num);
 }
 
 /* process the reply to get_members_info packet */
-void qq_process_group_cmd_get_members_info(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc)
+void qq_process_group_cmd_get_members_info(guint8 *data, gint len, PurpleConnection *gc)
 {
+	gint bytes;
+	gint num;
 	guint32 internal_group_id, member_uid;
 	guint16 unknown;
-	gint pascal_len, i;
 	qq_group *group;
 	qq_buddy *member;
 
 	g_return_if_fail(data != NULL && len > 0);
 
-	read_packet_dw(data, cursor, len, &internal_group_id);
+	bytes = 0;
+	bytes += qq_get32(&internal_group_id, data + bytes);
 	g_return_if_fail(internal_group_id > 0);
 
 	group = qq_group_find_by_id(gc, internal_group_id, QQ_INTERNAL_ID);
 	g_return_if_fail(group != NULL);
 
-	i = 0;
+	num = 0;
 	/* now starts the member info, as get buddy list reply */
-	while (*cursor < data + len) {
-		read_packet_dw(data, cursor, len, &member_uid);
+	while (bytes < len) {
+		bytes += qq_get32(&member_uid, data + bytes);
 		g_return_if_fail(member_uid > 0);
 		member = qq_group_find_member_by_uid(group, member_uid);
 		g_return_if_fail(member != NULL);
 
-		i++;
-		read_packet_w(data, cursor, len, &(member->face));
-		read_packet_b(data, cursor, len, &(member->age));
-		read_packet_b(data, cursor, len, &(member->gender));
-		pascal_len = convert_as_pascal_string(*cursor, &(member->nickname), QQ_CHARSET_DEFAULT);
-		*cursor += pascal_len;
-		read_packet_w(data, cursor, len, &unknown);
-		read_packet_b(data, cursor, len, &(member->flag1));
-		read_packet_b(data, cursor, len, &(member->comm_flag));
+		num++;
+		bytes += qq_get16(&(member->face), data + bytes);
+		bytes += qq_get8(&(member->age), data + bytes);
+		bytes += qq_get8(&(member->gender), data + bytes);
+		bytes += convert_as_pascal_string(data + bytes, &(member->nickname), QQ_CHARSET_DEFAULT);
+		bytes += qq_get16(&unknown, data + bytes);
+		bytes += qq_get8(&(member->flag1), data + bytes);
+		bytes += qq_get8(&(member->comm_flag), data + bytes);
 
 		member->last_refresh = time(NULL);
 	}
-        if(*cursor > (data + len)) {
-                         purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
-					 "group_cmd_get_members_info: Dangerous error! maybe protocol changed, notify developers!");
-        }
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "Group \"%s\" obtained %d member info\n", group->group_name_utf8, i);
+	if(bytes > len) {
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
+				"group_cmd_get_members_info: Dangerous error! maybe protocol changed, notify developers!");
+	}
+	purple_debug(PURPLE_DEBUG_INFO, "QQ", "Group \"%s\" obtained %d member info\n", group->group_name_utf8, num);
 }
--- a/libpurple/protocols/qq/group_info.h	Wed Jul 09 00:27:44 2008 +0000
+++ b/libpurple/protocols/qq/group_info.h	Wed Jul 09 00:32:18 2008 +0000
@@ -32,8 +32,8 @@
 void qq_send_cmd_group_get_group_info(PurpleConnection *gc, qq_group *group);
 void qq_send_cmd_group_get_online_members(PurpleConnection *gc, qq_group *group);
 void qq_send_cmd_group_get_members_info(PurpleConnection *gc, qq_group *group);
-void qq_process_group_cmd_get_group_info(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc);
-void qq_process_group_cmd_get_online_members(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc);
-void qq_process_group_cmd_get_members_info(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc);
+void qq_process_group_cmd_get_group_info(guint8 *data, gint len, PurpleConnection *gc);
+void qq_process_group_cmd_get_online_members(guint8 *data, gint len, PurpleConnection *gc);
+void qq_process_group_cmd_get_members_info(guint8 *data, gint len, PurpleConnection *gc);
 
 #endif
--- a/libpurple/protocols/qq/group_join.c	Wed Jul 09 00:27:44 2008 +0000
+++ b/libpurple/protocols/qq/group_join.c	Wed Jul 09 00:32:18 2008 +0000
@@ -64,8 +64,8 @@
 /* send packet to join a group without auth */
 void qq_send_cmd_group_join_group(PurpleConnection *gc, qq_group *group)
 {
-	guint8 *raw_data, *cursor;
-	gint bytes, data_len;
+	guint8 raw_data[16] = {0};
+	gint bytes = 0;
 
 	g_return_if_fail(group != NULL);
 
@@ -86,19 +86,11 @@
 		break;
 	}
 
-	data_len = 5;
-	raw_data = g_newa(guint8, data_len);
-	cursor = raw_data;
+	bytes = 0;
+	bytes += qq_put8(raw_data + bytes, QQ_GROUP_CMD_JOIN_GROUP);
+	bytes += qq_put32(raw_data + bytes, group->internal_group_id);
 
-	bytes = 0;
-	bytes += create_packet_b(raw_data, &cursor, QQ_GROUP_CMD_JOIN_GROUP);
-	bytes += create_packet_dw(raw_data, &cursor, group->internal_group_id);
-
-	if (bytes != data_len)
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
-			   "Fail create packet for %s\n", qq_group_cmd_get_desc(QQ_GROUP_CMD_JOIN_GROUP));
-	else
-		qq_send_group_cmd(gc, group, raw_data, data_len);
+	qq_send_group_cmd(gc, group, raw_data, bytes);
 }
 
 static void _qq_group_join_auth_with_gc_and_id(gc_and_uid *g, const gchar *reason_utf8)
@@ -145,7 +137,7 @@
 
 void qq_send_cmd_group_auth(PurpleConnection *gc, qq_group *group, guint8 opt, guint32 uid, const gchar *reason_utf8)
 {
-	guint8 *raw_data, *cursor;
+	guint8 *raw_data;
 	gchar *reason_qq;
 	gint bytes, data_len;
 
@@ -164,50 +156,42 @@
 
 	data_len = 10 + strlen(reason_qq) + 1;
 	raw_data = g_newa(guint8, data_len);
-	cursor = raw_data;
 
 	bytes = 0;
-	bytes += create_packet_b(raw_data, &cursor, QQ_GROUP_CMD_JOIN_GROUP_AUTH);
-	bytes += create_packet_dw(raw_data, &cursor, group->internal_group_id);
-	bytes += create_packet_b(raw_data, &cursor, opt);
-	bytes += create_packet_dw(raw_data, &cursor, uid);
-	bytes += create_packet_b(raw_data, &cursor, strlen(reason_qq));
-	bytes += create_packet_data(raw_data, &cursor, (guint8 *) reason_qq, strlen(reason_qq));
+	bytes += qq_put8(raw_data + bytes, QQ_GROUP_CMD_JOIN_GROUP_AUTH);
+	bytes += qq_put32(raw_data + bytes, group->internal_group_id);
+	bytes += qq_put8(raw_data + bytes, opt);
+	bytes += qq_put32(raw_data + bytes, uid);
+	bytes += qq_put8(raw_data + bytes, strlen(reason_qq));
+	bytes += qq_putdata(raw_data + bytes, (guint8 *) reason_qq, strlen(reason_qq));
 
-	if (bytes != data_len)
+	if (bytes != data_len) {
 		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
 			   "Fail create packet for %s\n", qq_group_cmd_get_desc(QQ_GROUP_CMD_JOIN_GROUP_AUTH));
-	else
-		qq_send_group_cmd(gc, group, raw_data, data_len);
+		return;
+	}
+
+	qq_send_group_cmd(gc, group, raw_data, data_len);
 }
 
 /* send a packet to exit a group */
 void qq_send_cmd_group_exit_group(PurpleConnection *gc, qq_group *group)
 {
-	guint8 *raw_data, *cursor;
-	gint bytes, data_len;
+	guint8 raw_data[16] = {0};
+	gint bytes = 0;
 
 	g_return_if_fail(group != NULL);
 
-	data_len = 5;
-	raw_data = g_newa(guint8, data_len);
-	cursor = raw_data;
+	bytes += qq_put8(raw_data + bytes, QQ_GROUP_CMD_EXIT_GROUP);
+	bytes += qq_put32(raw_data + bytes, group->internal_group_id);
 
-	bytes = 0;
-	bytes += create_packet_b(raw_data, &cursor, QQ_GROUP_CMD_EXIT_GROUP);
-	bytes += create_packet_dw(raw_data, &cursor, group->internal_group_id);
-
-	if (bytes != data_len)
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
-			   "Fail create packet for %s\n", qq_group_cmd_get_desc(QQ_GROUP_CMD_EXIT_GROUP));
-	else
-		qq_send_group_cmd(gc, group, raw_data, data_len);
+	qq_send_group_cmd(gc, group, raw_data, bytes);
 }
 
 /* If comes here, cmd is OK already */
-void qq_process_group_cmd_exit_group(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc)
+void qq_process_group_cmd_exit_group(guint8 *data, gint len, PurpleConnection *gc)
 {
-	gint bytes, expected_bytes;
+	gint bytes;
 	guint32 internal_group_id;
 	PurpleChat *chat;
 	qq_group *group;
@@ -216,96 +200,94 @@
 	g_return_if_fail(data != NULL && len > 0);
 	qd = (qq_data *) gc->proto_data;
 
-	bytes = 0;
-	expected_bytes = 4;
-	bytes += read_packet_dw(data, cursor, len, &internal_group_id);
+	if (len < 4) {
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
+			   "Invalid exit group reply, expect %d bytes, read %d bytes\n", 4, len);
+		return;
+	}
 
-	if (bytes == expected_bytes) {
-		group = qq_group_find_by_id(gc, internal_group_id, QQ_INTERNAL_ID);
-		if (group != NULL) {
-			chat =
-			    purple_blist_find_chat
+	bytes = 0;
+	bytes += qq_get32(&internal_group_id, data + bytes);
+
+	group = qq_group_find_by_id(gc, internal_group_id, QQ_INTERNAL_ID);
+	if (group != NULL) {
+		chat = purple_blist_find_chat
 			    (purple_connection_get_account(gc), g_strdup_printf("%d", group->external_group_id));
-			if (chat != NULL)
-				purple_blist_remove_chat(chat);
-			qq_group_delete_internal_record(qd, internal_group_id);
-		}
-		purple_notify_info(gc, _("QQ Qun Operation"), _("You have successfully left the group"), NULL);
-	} else {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
-			   "Invalid exit group reply, expect %d bytes, read %d bytes\n", expected_bytes, bytes);
+		if (chat != NULL)
+			purple_blist_remove_chat(chat);
+		qq_group_delete_internal_record(qd, internal_group_id);
 	}
+	purple_notify_info(gc, _("QQ Qun Operation"), _("You have successfully left the group"), NULL);
 }
 
 /* Process the reply to group_auth subcmd */
-void qq_process_group_cmd_join_group_auth(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc)
+void qq_process_group_cmd_join_group_auth(guint8 *data, gint len, PurpleConnection *gc)
 {
-	gint bytes, expected_bytes;
+	gint bytes;
 	guint32 internal_group_id;
 	qq_data *qd;
 
 	g_return_if_fail(data != NULL && len > 0);
 	qd = (qq_data *) gc->proto_data;
 
+	if (len < 4) {
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
+			   "Invalid join group reply, expect %d bytes, read %d bytes\n", 4, len);
+		return;
+	}
 	bytes = 0;
-	expected_bytes = 4;
-	bytes += read_packet_dw(data, cursor, len, &internal_group_id);
+	bytes += qq_get32(&internal_group_id, data + bytes);
 	g_return_if_fail(internal_group_id > 0);
 
-	if (bytes == expected_bytes)
-		purple_notify_info
-		    (gc, _("QQ Group Auth"),
+	purple_notify_info(gc, _("QQ Group Auth"),
 		     _("Your authorization request has been accepted by the QQ server"), NULL);
-	else
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
-			   "Invalid join group reply, expect %d bytes, read %d bytes\n", expected_bytes, bytes);
 }
 
 /* process group cmd reply "join group" */
-void qq_process_group_cmd_join_group(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc)
+void qq_process_group_cmd_join_group(guint8 *data, gint len, PurpleConnection *gc)
 {
-	gint bytes, expected_bytes;
+	gint bytes;
 	guint32 internal_group_id;
 	guint8 reply;
 	qq_group *group;
 
 	g_return_if_fail(data != NULL && len > 0);
 
-	bytes = 0;
-	expected_bytes = 5;
-	bytes += read_packet_dw(data, cursor, len, &internal_group_id);
-	bytes += read_packet_b(data, cursor, len, &reply);
-
-	if (bytes != expected_bytes) {
+	if (len < 5) {
 		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
-			   "Invalid join group reply, expect %d bytes, read %d bytes\n", expected_bytes, bytes);
+			   "Invalid join group reply, expect %d bytes, read %d bytes\n", 5, len);
 		return;
-	} else {		/* join group OK */
-		group = qq_group_find_by_id(gc, internal_group_id, QQ_INTERNAL_ID);
-		/* need to check if group is NULL or not. */
-		g_return_if_fail(group != NULL);
-		switch (reply) {
-		case QQ_GROUP_JOIN_OK:
-			purple_debug(PURPLE_DEBUG_INFO, "QQ", "Succeed joining group \"%s\"\n", group->group_name_utf8);
-			group->my_status = QQ_GROUP_MEMBER_STATUS_IS_MEMBER;
-			qq_group_refresh(gc, group);
-			/* this must be shown before getting online members */
-			qq_group_conv_show_window(gc, group);
-			qq_send_cmd_group_get_group_info(gc, group);
-			break;
-		case QQ_GROUP_JOIN_NEED_AUTH:
-			purple_debug(PURPLE_DEBUG_INFO, "QQ",
-				   "Fail joining group [%d] %s, needs authentication\n",
-				   group->external_group_id, group->group_name_utf8);
-			group->my_status = QQ_GROUP_MEMBER_STATUS_NOT_MEMBER;
-			qq_group_refresh(gc, group);
-			_qq_group_join_auth(gc, group);
-			break;
-		default:
-			purple_debug(PURPLE_DEBUG_INFO, "QQ",
-				   "Error joining group [%d] %s, unknown reply: 0x%02x\n",
-				   group->external_group_id, group->group_name_utf8, reply);
-		}
+	}
+	
+	bytes = 0;
+	bytes += qq_get32(&internal_group_id, data + bytes);
+	bytes += qq_get8(&reply, data + bytes);
+
+	/* join group OK */
+	group = qq_group_find_by_id(gc, internal_group_id, QQ_INTERNAL_ID);
+	/* need to check if group is NULL or not. */
+	g_return_if_fail(group != NULL);
+	switch (reply) {
+	case QQ_GROUP_JOIN_OK:
+		purple_debug(PURPLE_DEBUG_INFO, "QQ", "Succeed joining group \"%s\"\n", group->group_name_utf8);
+		group->my_status = QQ_GROUP_MEMBER_STATUS_IS_MEMBER;
+		qq_group_refresh(gc, group);
+		/* this must be shown before getting online members */
+		qq_group_conv_show_window(gc, group);
+		qq_send_cmd_group_get_group_info(gc, group);
+		break;
+	case QQ_GROUP_JOIN_NEED_AUTH:
+		purple_debug(PURPLE_DEBUG_INFO, "QQ",
+			   "Fail joining group [%d] %s, needs authentication\n",
+			   group->external_group_id, group->group_name_utf8);
+		group->my_status = QQ_GROUP_MEMBER_STATUS_NOT_MEMBER;
+		qq_group_refresh(gc, group);
+		_qq_group_join_auth(gc, group);
+		break;
+	default:
+		purple_debug(PURPLE_DEBUG_INFO, "QQ",
+			   "Error joining group [%d] %s, unknown reply: 0x%02x\n",
+			   group->external_group_id, group->group_name_utf8, reply);
 	}
 }
 
--- a/libpurple/protocols/qq/group_join.h	Wed Jul 09 00:27:44 2008 +0000
+++ b/libpurple/protocols/qq/group_join.h	Wed Jul 09 00:32:18 2008 +0000
@@ -46,8 +46,8 @@
 void qq_send_cmd_group_join_group(PurpleConnection *gc, qq_group *group);
 void qq_group_exit(PurpleConnection *gc, GHashTable *data);
 void qq_send_cmd_group_exit_group(PurpleConnection *gc, qq_group *group);
-void qq_process_group_cmd_exit_group(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc);
-void qq_process_group_cmd_join_group_auth(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc);
-void qq_process_group_cmd_join_group(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc);
+void qq_process_group_cmd_exit_group(guint8 *data, gint len, PurpleConnection *gc);
+void qq_process_group_cmd_join_group_auth(guint8 *data, gint len, PurpleConnection *gc);
+void qq_process_group_cmd_join_group(guint8 *data, gint len, PurpleConnection *gc);
 
 #endif
--- a/libpurple/protocols/qq/group_network.c	Wed Jul 09 00:27:44 2008 +0000
+++ b/libpurple/protocols/qq/group_network.c	Wed Jul 09 00:32:18 2008 +0000
@@ -39,7 +39,7 @@
 #include "group_opt.h"
 #include "group_search.h"
 #include "header_info.h"
-#include "send_core.h"
+#include "qq_network.h"
 #include "utils.h"
 
 enum {
@@ -81,12 +81,12 @@
 }
 
 /* default process of reply error */
-static void _qq_process_group_cmd_reply_error_default(guint8 reply, guint8 *cursor, gint len, PurpleConnection *gc)
+static void _qq_process_group_cmd_reply_error_default(guint8 reply, guint8 *data, gint len, PurpleConnection *gc)
 {
 	gchar *msg, *msg_utf8;
-	g_return_if_fail(cursor != NULL && len > 0);
+	g_return_if_fail(data != NULL && len > 0);
 
-	msg = g_strndup((gchar *) cursor, len);	/* it will append 0x00 */
+	msg = g_strndup((gchar *) data, len);	/* it will append 0x00 */
 	msg_utf8 = qq_to_utf8(msg, QQ_CHARSET_DEFAULT);
 	g_free(msg);
 	msg = g_strdup_printf(_("Code [0x%02X]: %s"), reply, msg_utf8);
@@ -96,14 +96,13 @@
 }
 
 /* default process, dump only */
-static void _qq_process_group_cmd_reply_default(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc)
+static void _qq_process_group_cmd_reply_default(guint8 *data, gint len, PurpleConnection *gc)
 {
-	gchar *hex_dump;
 	g_return_if_fail(data != NULL && len > 0);
 
-	hex_dump = hex_dump_to_str(data, len);
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "Dump unprocessed group cmd reply:\n%s", hex_dump);
-	g_free(hex_dump);
+	qq_hex_dump(PURPLE_DEBUG_INFO, "QQ",
+		data, len, 
+		"Dump unprocessed group cmd reply:");
 }
 
 /* The lower layer command of send group cmd */
@@ -116,7 +115,7 @@
 
 	qd = (qq_data *) gc->proto_data;
 
-	qq_send_cmd(gc, QQ_CMD_GROUP_CMD, TRUE, 0, TRUE, raw_data, data_len);
+	qq_send_cmd(qd, QQ_CMD_GROUP_CMD, raw_data, data_len);
 
 	p = g_new0(group_packet, 1);
 
@@ -136,7 +135,7 @@
 	qq_data *qd;
 	gint len, bytes;
 	guint32 internal_group_id;
-	guint8 *data, *cursor, sub_cmd, reply;
+	guint8 *data, sub_cmd, reply;
 
 	g_return_if_fail(buf != NULL && buf_len != 0);
 
@@ -149,102 +148,101 @@
 		return;
 	}
 
-	if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
-		if (len <= 2) {
-			purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Group cmd reply is too short, only %d bytes\n", len);
-			return;
-		}
+	if ( !qq_decrypt(buf, buf_len, qd->session_key, data, &len) ) {
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt group cmd reply\n");
+		return;
+	}
 
-		bytes = 0;
-		cursor = data;
-		bytes += read_packet_b(data, &cursor, len, &sub_cmd);
-		bytes += read_packet_b(data, &cursor, len, &reply);
-
-		group = qq_group_find_by_id(gc, internal_group_id, QQ_INTERNAL_ID);
+	if (len <= 2) {
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Group cmd reply is too short, only %d bytes\n", len);
+		return;
+	}
 
-		if (reply != QQ_GROUP_CMD_REPLY_OK) {
-			purple_debug(PURPLE_DEBUG_WARNING, "QQ",
-				   "Group cmd reply says cmd %s fails\n", qq_group_cmd_get_desc(sub_cmd));
+	bytes = 0;
+	bytes += qq_get8(&sub_cmd, data + bytes);
+	bytes += qq_get8(&reply, data + bytes);
 
-			if (group != NULL)
-				qq_set_pending_id(&qd->joining_groups, group->external_group_id, FALSE);
+	group = qq_group_find_by_id(gc, internal_group_id, QQ_INTERNAL_ID);
 
-			switch (reply) {	/* this should be all errors */
-			case QQ_GROUP_CMD_REPLY_NOT_MEMBER:
-				if (group != NULL) {
-					purple_debug(PURPLE_DEBUG_WARNING,
-						   "QQ",
-						   "You are not a member of group \"%s\"\n", group->group_name_utf8);
-					group->my_status = QQ_GROUP_MEMBER_STATUS_NOT_MEMBER;
-					qq_group_refresh(gc, group);
-				}
-				break;
-			case QQ_GROUP_CMD_REPLY_SEARCH_ERROR:
-				if (qd->roomlist != NULL) {
-					if (purple_roomlist_get_in_progress(qd->roomlist))
-						purple_roomlist_set_in_progress(qd->roomlist, FALSE);
-				}
-				_qq_process_group_cmd_reply_error_default(reply, cursor, len - bytes, gc);
-				break;
-			default:
-				_qq_process_group_cmd_reply_error_default(reply, cursor, len - bytes, gc);
-			}
-			return;
-		}
+	if (reply != QQ_GROUP_CMD_REPLY_OK) {
+		purple_debug(PURPLE_DEBUG_WARNING, "QQ",
+			   "Group cmd reply says cmd %s fails\n", qq_group_cmd_get_desc(sub_cmd));
+
+		if (group != NULL)
+			qq_set_pending_id(&qd->joining_groups, group->external_group_id, FALSE);
 
-		/* seems ok so far, so we process the reply according to sub_cmd */
-		switch (sub_cmd) {
-		case QQ_GROUP_CMD_GET_GROUP_INFO:
-			qq_process_group_cmd_get_group_info(data, &cursor, len, gc);
+		switch (reply) {	/* this should be all errors */
+		case QQ_GROUP_CMD_REPLY_NOT_MEMBER:
 			if (group != NULL) {
-				qq_send_cmd_group_get_members_info(gc, group);
-				qq_send_cmd_group_get_online_members(gc, group);
+				purple_debug(PURPLE_DEBUG_WARNING,
+					   "QQ",
+					   "You are not a member of group \"%s\"\n", group->group_name_utf8);
+				group->my_status = QQ_GROUP_MEMBER_STATUS_NOT_MEMBER;
+				qq_group_refresh(gc, group);
 			}
 			break;
-		case QQ_GROUP_CMD_CREATE_GROUP:
-			qq_group_process_create_group_reply(data, &cursor, len, gc);
-			break;
-		case QQ_GROUP_CMD_MODIFY_GROUP_INFO:
-			qq_group_process_modify_info_reply(data, &cursor, len, gc);
-			break;
-		case QQ_GROUP_CMD_MEMBER_OPT:
-			qq_group_process_modify_members_reply(data, &cursor, len, gc);
-			break;
-		case QQ_GROUP_CMD_ACTIVATE_GROUP:
-			qq_group_process_activate_group_reply(data, &cursor, len, gc);
-			break;
-		case QQ_GROUP_CMD_SEARCH_GROUP:
-			qq_process_group_cmd_search_group(data, &cursor, len, gc);
-			break;
-		case QQ_GROUP_CMD_JOIN_GROUP:
-			qq_process_group_cmd_join_group(data, &cursor, len, gc);
-			break;
-		case QQ_GROUP_CMD_JOIN_GROUP_AUTH:
-			qq_process_group_cmd_join_group_auth(data, &cursor, len, gc);
-			break;
-		case QQ_GROUP_CMD_EXIT_GROUP:
-			qq_process_group_cmd_exit_group(data, &cursor, len, gc);
-			break;
-		case QQ_GROUP_CMD_SEND_MSG:
-			qq_process_group_cmd_im(data, &cursor, len, gc);
-			break;
-		case QQ_GROUP_CMD_GET_ONLINE_MEMBER:
-			qq_process_group_cmd_get_online_members(data, &cursor, len, gc);
-			if (group != NULL)
-				qq_group_conv_refresh_online_member(gc, group);
-			break;
-		case QQ_GROUP_CMD_GET_MEMBER_INFO:
-			qq_process_group_cmd_get_members_info(data, &cursor, len, gc);
-			if (group != NULL)
-				qq_group_conv_refresh_online_member(gc, group);
+		case QQ_GROUP_CMD_REPLY_SEARCH_ERROR:
+			if (qd->roomlist != NULL) {
+				if (purple_roomlist_get_in_progress(qd->roomlist))
+					purple_roomlist_set_in_progress(qd->roomlist, FALSE);
+			}
+			_qq_process_group_cmd_reply_error_default(reply, data + bytes, len - bytes, gc);
 			break;
 		default:
-			purple_debug(PURPLE_DEBUG_WARNING, "QQ",
-				   "Group cmd %s is processed by default\n", qq_group_cmd_get_desc(sub_cmd));
-			_qq_process_group_cmd_reply_default(data, &cursor, len, gc);
+			_qq_process_group_cmd_reply_error_default(reply, data + bytes, len - bytes, gc);
+		}
+		return;
+	}
+
+	/* seems ok so far, so we process the reply according to sub_cmd */
+	switch (sub_cmd) {
+	case QQ_GROUP_CMD_GET_GROUP_INFO:
+		qq_process_group_cmd_get_group_info(data + bytes, len - bytes, gc);
+		if (group != NULL) {
+			qq_send_cmd_group_get_members_info(gc, group);
+			qq_send_cmd_group_get_online_members(gc, group);
 		}
-
-	} else {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt group cmd reply\n");
+		break;
+	case QQ_GROUP_CMD_CREATE_GROUP:
+		qq_group_process_create_group_reply(data + bytes, len - bytes, gc);
+		break;
+	case QQ_GROUP_CMD_MODIFY_GROUP_INFO:
+		qq_group_process_modify_info_reply(data + bytes, len - bytes, gc);
+		break;
+	case QQ_GROUP_CMD_MEMBER_OPT:
+		qq_group_process_modify_members_reply(data + bytes, len - bytes, gc);
+		break;
+	case QQ_GROUP_CMD_ACTIVATE_GROUP:
+		qq_group_process_activate_group_reply(data + bytes, len - bytes, gc);
+		break;
+	case QQ_GROUP_CMD_SEARCH_GROUP:
+		qq_process_group_cmd_search_group(data + bytes, len - bytes, gc);
+		break;
+	case QQ_GROUP_CMD_JOIN_GROUP:
+		qq_process_group_cmd_join_group(data + bytes, len - bytes, gc);
+		break;
+	case QQ_GROUP_CMD_JOIN_GROUP_AUTH:
+		qq_process_group_cmd_join_group_auth(data + bytes, len - bytes, gc);
+		break;
+	case QQ_GROUP_CMD_EXIT_GROUP:
+		qq_process_group_cmd_exit_group(data + bytes, len - bytes, gc);
+		break;
+	case QQ_GROUP_CMD_SEND_MSG:
+		qq_process_group_cmd_im(data + bytes, len - bytes, gc);
+		break;
+	case QQ_GROUP_CMD_GET_ONLINE_MEMBER:
+		qq_process_group_cmd_get_online_members(data + bytes, len - bytes, gc);
+		if (group != NULL)
+			qq_group_conv_refresh_online_member(gc, group);
+		break;
+	case QQ_GROUP_CMD_GET_MEMBER_INFO:
+		qq_process_group_cmd_get_members_info(data + bytes, len - bytes, gc);
+		if (group != NULL)
+			qq_group_conv_refresh_online_member(gc, group);
+		break;
+	default:
+		purple_debug(PURPLE_DEBUG_WARNING, "QQ",
+			   "Group cmd %s is processed by default\n", qq_group_cmd_get_desc(sub_cmd));
+		_qq_process_group_cmd_reply_default(data + bytes, len, gc);
 	}
 }
--- a/libpurple/protocols/qq/group_opt.c	Wed Jul 09 00:27:44 2008 +0000
+++ b/libpurple/protocols/qq/group_opt.c	Wed Jul 09 00:32:18 2008 +0000
@@ -57,22 +57,24 @@
 
 static void _qq_group_member_opt(PurpleConnection *gc, qq_group *group, gint operation, guint32 *members)
 {
-	guint8 *data, *cursor;
+	guint8 *data;
 	gint i, count, data_len;
+	gint bytes;
 	g_return_if_fail(members != NULL);
 
-	for (i = 0; members[i] != 0xffffffff; i++) {;
+	for (count = 0; members[count] != 0xffffffff; count++) {;
 	}
-	count = i;
 	data_len = 6 + count * 4;
 	data = g_newa(guint8, data_len);
-	cursor = data;
-	create_packet_b(data, &cursor, QQ_GROUP_CMD_MEMBER_OPT);
-	create_packet_dw(data, &cursor, group->internal_group_id);
-	create_packet_b(data, &cursor, operation);
+	
+	bytes = 0;
+	bytes += qq_put8(data + bytes, QQ_GROUP_CMD_MEMBER_OPT);
+	bytes += qq_put32(data + bytes, group->internal_group_id);
+	bytes += qq_put8(data + bytes, operation);
 	for (i = 0; i < count; i++)
-		create_packet_dw(data, &cursor, members[i]);
-	qq_send_group_cmd(gc, group, data, data_len);
+		bytes += qq_put32(data + bytes, members[i]);
+
+	qq_send_group_cmd(gc, group, data, bytes);
 }
 
 static void _qq_group_do_nothing_with_struct(group_member_opt *g)
@@ -97,11 +99,11 @@
 
 	qq_send_packet_get_info(g->gc, g->member, TRUE);	/* we want to see window */
 	purple_request_action(g->gc, NULL, _("Do you want to approve the request?"), "",
-					PURPLE_DEFAULT_ACTION_NONE,
-					purple_connection_get_account(g->gc), NULL, NULL,
-					g, 2,
-					_("Reject"), G_CALLBACK(qq_group_reject_application_with_struct),
-					_("Approve"), G_CALLBACK(qq_group_approve_application_with_struct));
+				PURPLE_DEFAULT_ACTION_NONE,
+				purple_connection_get_account(g->gc), NULL, NULL,
+				g, 2,
+				_("Reject"), G_CALLBACK(qq_group_reject_application_with_struct),
+				_("Approve"), G_CALLBACK(qq_group_approve_application_with_struct));
 }
 
 void qq_group_reject_application_with_struct(group_member_opt *g)
@@ -193,13 +195,15 @@
 		_qq_group_member_opt(gc, group, QQ_GROUP_MEMBER_ADD, add_members);
 }
 
-void qq_group_process_modify_members_reply(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc)
+void qq_group_process_modify_members_reply(guint8 *data, gint len, PurpleConnection *gc)
 {
+	gint bytes;
 	guint32 internal_group_id;
 	qq_group *group;
 	g_return_if_fail(data != NULL);
 
-	read_packet_dw(data, cursor, len, &internal_group_id);
+	bytes = 0;
+	bytes += qq_get32(&internal_group_id, data + bytes);
 	g_return_if_fail(internal_group_id > 0);
 
 	/* we should have its info locally */
@@ -213,8 +217,9 @@
 
 void qq_group_modify_info(PurpleConnection *gc, qq_group *group)
 {
-	gint data_len, data_written;
-	guint8 *data, *cursor;
+	guint8 *data;
+	gint data_len;
+	gint bytes;
 	gchar *group_name, *group_desc, *notice;
 
 	g_return_if_fail(group != NULL);
@@ -228,47 +233,50 @@
 	    + 1 + strlen(notice);
 
 	data = g_newa(guint8, data_len);
-	cursor = data;
-	data_written = 0;
+	bytes = 0;
 	/* 000-000 */
-	data_written += create_packet_b(data, &cursor, QQ_GROUP_CMD_MODIFY_GROUP_INFO);
+	bytes += qq_put8(data + bytes, QQ_GROUP_CMD_MODIFY_GROUP_INFO);
 	/* 001-004 */
-	data_written += create_packet_dw(data, &cursor, group->internal_group_id);
+	bytes += qq_put32(data + bytes, group->internal_group_id);
 	/* 005-005 */
-	data_written += create_packet_b(data, &cursor, 0x01);
+	bytes += qq_put8(data + bytes, 0x01);
 	/* 006-006 */
-	data_written += create_packet_b(data, &cursor, group->auth_type);
+	bytes += qq_put8(data + bytes, group->auth_type);
 	/* 007-008 */
-	data_written += create_packet_w(data, &cursor, 0x0000);
+	bytes += qq_put16(data + bytes, 0x0000);
 	/* 009-010 */
-	data_written += create_packet_w(data, &cursor, group->group_category);
+	bytes += qq_put16(data + bytes, group->group_category);
 
-	data_written += create_packet_b(data, &cursor, strlen(group_name));
-	data_written += create_packet_data(data, &cursor, (guint8 *) group_name, strlen(group_name));
+	bytes += qq_put8(data + bytes, strlen(group_name));
+	bytes += qq_putdata(data + bytes, (guint8 *) group_name, strlen(group_name));
 
-	data_written += create_packet_w(data, &cursor, 0x0000);
+	bytes += qq_put16(data + bytes, 0x0000);
 
-	data_written += create_packet_b(data, &cursor, strlen(notice));
-	data_written += create_packet_data(data, &cursor, (guint8 *) notice, strlen(notice));
+	bytes += qq_put8(data + bytes, strlen(notice));
+	bytes += qq_putdata(data+ bytes, (guint8 *) notice, strlen(notice));
 
-	data_written += create_packet_b(data, &cursor, strlen(group_desc));
-	data_written += create_packet_data(data, &cursor, (guint8 *) group_desc, strlen(group_desc));
+	bytes += qq_put8(data + bytes, strlen(group_desc));
+	bytes += qq_putdata(data + bytes, (guint8 *) group_desc, strlen(group_desc));
 
-	if (data_written != data_len)
+	if (bytes != data_len)	{
 		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
 			   "Fail to create group_modify_info packet, expect %d bytes, wrote %d bytes\n",
-			   data_len, data_written);
-	else
-		qq_send_group_cmd(gc, group, data, data_len);
+			   data_len, bytes);
+		return;
+	}
+
+	qq_send_group_cmd(gc, group, data, bytes);
 }
 
-void qq_group_process_modify_info_reply(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc)
+void qq_group_process_modify_info_reply(guint8 *data, gint len, PurpleConnection *gc)
 {
+	gint bytes;
 	guint32 internal_group_id;
 	qq_group *group;
 	g_return_if_fail(data != NULL);
 
-	read_packet_dw(data, cursor, len, &internal_group_id);
+	bytes = 0;
+	bytes += qq_get32(&internal_group_id, data + bytes);
 	g_return_if_fail(internal_group_id > 0);
 
 	/* we should have its info locally */
@@ -284,42 +292,44 @@
 /* we create a very simple group first, and then let the user to modify */
 void qq_group_create_with_name(PurpleConnection *gc, const gchar *name)
 {
-	gint data_len, data_written;
-	guint8 *data, *cursor;
+	gint data_len;
+	guint8 *data;
+	gint bytes;
 	qq_data *qd;
 	g_return_if_fail(name != NULL);
 
 	qd = (qq_data *) gc->proto_data;
 	data_len = 7 + 1 + strlen(name) + 2 + 1 + 1 + 4;
 	data = g_newa(guint8, data_len);
-	cursor = data;
 
-	data_written = 0;
+	bytes = 0;
 	/* we create the simpleset group, only group name is given */
 	/* 000 */
-	data_written += create_packet_b(data, &cursor, QQ_GROUP_CMD_CREATE_GROUP);
+	bytes += qq_put8(data + bytes, QQ_GROUP_CMD_CREATE_GROUP);
 	/* 001 */
-	data_written += create_packet_b(data, &cursor, QQ_GROUP_TYPE_PERMANENT);
+	bytes += qq_put8(data + bytes, QQ_GROUP_TYPE_PERMANENT);
 	/* 002 */
-	data_written += create_packet_b(data, &cursor, QQ_GROUP_AUTH_TYPE_NEED_AUTH);
+	bytes += qq_put8(data + bytes, QQ_GROUP_AUTH_TYPE_NEED_AUTH);
 	/* 003-004 */
-	data_written += create_packet_w(data, &cursor, 0x0000);
+	bytes += qq_put16(data + bytes, 0x0000);
 	/* 005-006 */
-	data_written += create_packet_w(data, &cursor, 0x0003);
+	bytes += qq_put16(data + bytes, 0x0003);
 	/* 007 */
-	data_written += create_packet_b(data, &cursor, strlen(name));
-	data_written += create_packet_data(data, &cursor, (guint8 *) name, strlen(name));
-	data_written += create_packet_w(data, &cursor, 0x0000);
-	data_written += create_packet_b(data, &cursor, 0x00);	/* no group notice */
-	data_written += create_packet_b(data, &cursor, 0x00);	/* no group desc */
-	data_written += create_packet_dw(data, &cursor, qd->uid);	/* I am member of coz */
+	bytes += qq_put8(data + bytes, strlen(name));
+	bytes += qq_putdata(data + bytes, (guint8 *) name, strlen(name));
+	bytes += qq_put16(data + bytes, 0x0000);
+	bytes += qq_put8(data + bytes, 0x00);	/* no group notice */
+	bytes += qq_put8(data + bytes, 0x00);	/* no group desc */
+	bytes += qq_put32(data + bytes, qd->uid);	/* I am member of coz */
 
-	if (data_written != data_len)
+	if (bytes != data_len) {
 		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
 			   "Fail create create_group packet, expect %d bytes, written %d bytes\n",
-			   data_len, data_written);
-	else
-		qq_send_group_cmd(gc, NULL, data, data_len);
+			   data_len, bytes);
+		return;
+	}
+
+	qq_send_group_cmd(gc, NULL, data, bytes);
 }
 
 static void qq_group_setup_with_gc_and_uid(gc_and_uid *g)
@@ -335,8 +345,9 @@
 	g_free(g);
 }
 
-void qq_group_process_create_group_reply(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc)
+void qq_group_process_create_group_reply(guint8 *data, gint len, PurpleConnection *gc)
 {
+	gint bytes;
 	guint32 internal_group_id, external_group_id;
 	qq_group *group;
 	gc_and_uid *g;
@@ -346,8 +357,9 @@
 	g_return_if_fail(gc->proto_data != NULL);
 	qd = (qq_data *) gc->proto_data;
 
-	read_packet_dw(data, cursor, len, &internal_group_id);
-	read_packet_dw(data, cursor, len, &external_group_id);
+	bytes = 0;
+	bytes += qq_get32(&internal_group_id, data + bytes);
+	bytes += qq_get32(&external_group_id, data + bytes);
 	g_return_if_fail(internal_group_id > 0 && external_group_id);
 
 	group = qq_group_create_internal_record(gc, internal_group_id, external_group_id, NULL);
@@ -378,36 +390,29 @@
 /* we have to activate group after creation, otherwise the group can not be searched */
 void qq_group_activate_group(PurpleConnection *gc, guint32 internal_group_id)
 {
-	gint data_len, data_written;
-	guint8 *data, *cursor;
+	guint8 data[16] = {0};
+	gint bytes = 0;
 	g_return_if_fail(internal_group_id > 0);
 
-	data_len = 5;
-	data = g_newa(guint8, data_len);
-	cursor = data;
-
-	data_written = 0;
+	bytes = 0;
 	/* we create the simplest group, only group name is given */
 	/* 000 */
-	data_written += create_packet_b(data, &cursor, QQ_GROUP_CMD_ACTIVATE_GROUP);
+	bytes += qq_put8(data + bytes, QQ_GROUP_CMD_ACTIVATE_GROUP);
 	/* 001-005 */
-	data_written += create_packet_dw(data, &cursor, internal_group_id);
+	bytes += qq_put32(data + bytes, internal_group_id);
 
-	if (data_written != data_len)
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
-			   "Fail create activate_group packet, expect %d bytes, written %d bytes\n",
-			   data_len, data_written);
-	else
-		qq_send_group_cmd(gc, NULL, data, data_len);
+	qq_send_group_cmd(gc, NULL, data, bytes);
 }
 
-void qq_group_process_activate_group_reply(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc)
+void qq_group_process_activate_group_reply(guint8 *data, gint len, PurpleConnection *gc)
 {
+	gint bytes;
 	guint32 internal_group_id;
 	qq_group *group;
 	g_return_if_fail(data != NULL);
 
-	read_packet_dw(data, cursor, len, &internal_group_id);
+	bytes = 0;
+	bytes += qq_get32(&internal_group_id, data + bytes);
 	g_return_if_fail(internal_group_id > 0);
 
 	/* we should have its info locally */
--- a/libpurple/protocols/qq/group_opt.h	Wed Jul 09 00:27:44 2008 +0000
+++ b/libpurple/protocols/qq/group_opt.h	Wed Jul 09 00:32:18 2008 +0000
@@ -54,12 +54,12 @@
 void qq_group_reject_application_with_struct(group_member_opt *g);
 void qq_group_search_application_with_struct(group_member_opt *g);
 
-void qq_group_process_modify_info_reply(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc);
-void qq_group_process_modify_members_reply(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc);
+void qq_group_process_modify_info_reply(guint8 *data, gint len, PurpleConnection *gc);
+void qq_group_process_modify_members_reply(guint8 *data, gint len, PurpleConnection *gc);
 void qq_group_manage_group(PurpleConnection *gc, GHashTable *data);
 void qq_group_create_with_name(PurpleConnection *gc, const gchar *name);
 void qq_group_activate_group(PurpleConnection *gc, guint32 internal_group_id);
-void qq_group_process_activate_group_reply(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc);
-void qq_group_process_create_group_reply(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc);
+void qq_group_process_activate_group_reply(guint8 *data, gint len, PurpleConnection *gc);
+void qq_group_process_create_group_reply(guint8 *data, gint len, PurpleConnection *gc);
 
 #endif
--- a/libpurple/protocols/qq/group_search.c	Wed Jul 09 00:27:44 2008 +0000
+++ b/libpurple/protocols/qq/group_search.c	Wed Jul 09 00:32:18 2008 +0000
@@ -43,24 +43,18 @@
 /* send packet to search for qq_group */
 void qq_send_cmd_group_search_group(PurpleConnection *gc, guint32 external_group_id)
 {
-	guint8 *raw_data, *cursor, type;
-	gint bytes, data_len;
+	guint8 raw_data[16] = {0};
+	gint bytes = 0;
+	guint8 type;
 
-	data_len = 6;
-	raw_data = g_newa(guint8, data_len);
-	cursor = raw_data;
 	type = (external_group_id == 0x00000000) ? QQ_GROUP_SEARCH_TYPE_DEMO : QQ_GROUP_SEARCH_TYPE_BY_ID;
 
 	bytes = 0;
-	bytes += create_packet_b(raw_data, &cursor, QQ_GROUP_CMD_SEARCH_GROUP);
-	bytes += create_packet_b(raw_data, &cursor, type);
-	bytes += create_packet_dw(raw_data, &cursor, external_group_id);
+	bytes += qq_put8(raw_data + bytes, QQ_GROUP_CMD_SEARCH_GROUP);
+	bytes += qq_put8(raw_data + bytes, type);
+	bytes += qq_put32(raw_data + bytes, external_group_id);
 
-	if (bytes != data_len)
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
-			   "Fail create packet for %s\n", qq_group_cmd_get_desc(QQ_GROUP_CMD_SEARCH_GROUP));
-	else
-		qq_send_group_cmd(gc, NULL, raw_data, data_len);
+	qq_send_group_cmd(gc, NULL, raw_data, bytes);
 }
 
 static void _qq_setup_roomlist(qq_data *qd, qq_group *group)
@@ -89,55 +83,50 @@
 }
 
 /* process group cmd reply "search group" */
-void qq_process_group_cmd_search_group(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc)
+void qq_process_group_cmd_search_group(guint8 *data, gint len, PurpleConnection *gc)
 {
+	gint bytes;
 	guint8 search_type;
 	guint16 unknown;
-	gint bytes, pascal_len;
+	qq_group group;
 	qq_data *qd;
-	qq_group *group;
 	GSList *pending_id;
 
 	g_return_if_fail(data != NULL && len > 0);
 	qd = (qq_data *) gc->proto_data;
 
-	read_packet_b(data, cursor, len, &search_type);
-	group = g_newa(qq_group, 1);
+	bytes = 0;
+	bytes += qq_get8(&search_type, data + bytes);
 
 	/* now it starts with group_info_entry */
-	bytes = 0;
-	bytes += read_packet_dw(data, cursor, len, &(group->internal_group_id));
-	bytes += read_packet_dw(data, cursor, len, &(group->external_group_id));
-	bytes += read_packet_b(data, cursor, len, &(group->group_type));
-	bytes += read_packet_w(data, cursor, len, &(unknown));
-	bytes += read_packet_w(data, cursor, len, &(unknown));
-	bytes += read_packet_dw(data, cursor, len, &(group->creator_uid));
-	bytes += read_packet_w(data, cursor, len, &(unknown));
-	bytes += read_packet_w(data, cursor, len, &(unknown));
-	bytes += read_packet_w(data, cursor, len, &(unknown));
-	bytes += read_packet_dw(data, cursor, len, &(group->group_category));
-	pascal_len = convert_as_pascal_string(*cursor, &(group->group_name_utf8), QQ_CHARSET_DEFAULT);
-	bytes += pascal_len;
-	*cursor += pascal_len;
-	bytes += read_packet_w(data, cursor, len, &(unknown));
-	bytes += read_packet_b(data, cursor, len, &(group->auth_type));
-	pascal_len = convert_as_pascal_string(*cursor, &(group->group_desc_utf8), QQ_CHARSET_DEFAULT);
-	bytes += pascal_len;
-	*cursor += pascal_len;
+	bytes += qq_get32(&(group.internal_group_id), data + bytes);
+	bytes += qq_get32(&(group.external_group_id), data + bytes);
+	bytes += qq_get8(&(group.group_type), data + bytes);
+	bytes += qq_get16(&(unknown), data + bytes);
+	bytes += qq_get16(&(unknown), data + bytes);
+	bytes += qq_get32(&(group.creator_uid), data + bytes);
+	bytes += qq_get16(&(unknown), data + bytes);
+	bytes += qq_get16(&(unknown), data + bytes);
+	bytes += qq_get16(&(unknown), data + bytes);
+	bytes += qq_get32(&(group.group_category), data + bytes);
+	bytes += convert_as_pascal_string(data + bytes, &(group.group_name_utf8), QQ_CHARSET_DEFAULT);
+	bytes += qq_get16(&(unknown), data + bytes);
+	bytes += qq_get8(&(group.auth_type), data + bytes);
+	bytes += convert_as_pascal_string(data + bytes, &(group.group_desc_utf8), QQ_CHARSET_DEFAULT);
 	/* end of one qq_group */
-        if(*cursor != (data + len)) {
-                         purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
-					 "group_cmd_search_group: Dangerous error! maybe protocol changed, notify developers!");
-        }
+	if(bytes != len) {
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
+			"group_cmd_search_group: Dangerous error! maybe protocol changed, notify developers!");
+	}
 
-	pending_id = qq_get_pending_id(qd->joining_groups, group->external_group_id);
+	pending_id = qq_get_pending_id(qd->joining_groups, group.external_group_id);
 	if (pending_id != NULL) {
-		qq_set_pending_id(&qd->joining_groups, group->external_group_id, FALSE);
-		if (qq_group_find_by_id(gc, group->internal_group_id, QQ_INTERNAL_ID) == NULL)
+		qq_set_pending_id(&qd->joining_groups, group.external_group_id, FALSE);
+		if (qq_group_find_by_id(gc, group.internal_group_id, QQ_INTERNAL_ID) == NULL)
 			qq_group_create_internal_record(gc, 
-					group->internal_group_id, group->external_group_id, group->group_name_utf8);
-		qq_send_cmd_group_join_group(gc, group);
+					group.internal_group_id, group.external_group_id, group.group_name_utf8);
+		qq_send_cmd_group_join_group(gc, &group);
 	} else {
-		_qq_setup_roomlist(qd, group);
+		_qq_setup_roomlist(qd, &group);
 	}
 }
--- a/libpurple/protocols/qq/group_search.h	Wed Jul 09 00:27:44 2008 +0000
+++ b/libpurple/protocols/qq/group_search.h	Wed Jul 09 00:32:18 2008 +0000
@@ -29,6 +29,6 @@
 #include "connection.h"
 
 void qq_send_cmd_group_search_group(PurpleConnection *gc, guint32 external_group_id);
-void qq_process_group_cmd_search_group(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc);
+void qq_process_group_cmd_search_group(guint8 *data, gint len, PurpleConnection *gc);
 
 #endif
--- a/libpurple/protocols/qq/im.c	Wed Jul 09 00:27:44 2008 +0000
+++ b/libpurple/protocols/qq/im.c	Wed Jul 09 00:32:18 2008 +0000
@@ -40,19 +40,16 @@
 #include "header_info.h"
 #include "im.h"
 #include "packet_parse.h"
-#include "send_core.h"
+#include "qq_network.h"
 #include "send_file.h"
 #include "utils.h"
 
 #define QQ_SEND_IM_REPLY_OK       0x00
 #define DEFAULT_FONT_NAME_LEN 	  4
 
-/* a debug function */
-void _qq_show_packet(const gchar *desc, const guint8 *buf, gint len);
-
 enum
 {
-        QQ_NORMAL_IM_TEXT = 0x000b,
+	QQ_NORMAL_IM_TEXT = 0x000b,
 	QQ_NORMAL_IM_FILE_REQUEST_TCP = 0x0001,
 	QQ_NORMAL_IM_FILE_APPROVE_TCP = 0x0003,
 	QQ_NORMAL_IM_FILE_REJECT_TCP = 0x0005,
@@ -121,9 +118,9 @@
 #define DEFAULT_FONT_NAME "\0xcb\0xce\0xcc\0xe5"
 
 guint8 *qq_get_send_im_tail(const gchar *font_color,
-			    const gchar *font_size,
-			    const gchar *font_name,
-			    gboolean is_bold, gboolean is_italic, gboolean is_underline, gint tail_len)
+		const gchar *font_size,
+		const gchar *font_name,
+		gboolean is_bold, gboolean is_italic, gboolean is_underline, gint tail_len)
 {
 	gchar *s1;
 	unsigned char *rgb;
@@ -141,7 +138,7 @@
 	send_im_tail = g_new0(guint8, tail_len);
 
 	g_strlcpy((gchar *) (send_im_tail + QQ_SEND_IM_AFTER_MSG_HEADER_LEN),
-		  font_name, tail_len - QQ_SEND_IM_AFTER_MSG_HEADER_LEN);
+			font_name, tail_len - QQ_SEND_IM_AFTER_MSG_HEADER_LEN);
 	send_im_tail[tail_len - 1] = (guint8) tail_len;
 
 	send_im_tail[0] = 0x00;
@@ -182,39 +179,39 @@
 	send_im_tail[5] = 0x00;
 	send_im_tail[6] = 0x86;
 	send_im_tail[7] = 0x22;	/* encoding, 0x8622=GB, 0x0000=EN, define BIG5 support here */
-	_qq_show_packet("QQ_MESG", send_im_tail, tail_len);
+	qq_show_packet("QQ_MESG", send_im_tail, tail_len);
 	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";
-	default:
-		return "QQ_RECV_IM_UNKNOWN";
+		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";
+		default:
+			return "QQ_RECV_IM_UNKNOWN";
 	}
 }
 
@@ -222,27 +219,29 @@
  * we send an ACK which is the first 16 bytes of incoming packet */
 static void _qq_send_packet_recv_im_ack(PurpleConnection *gc, guint16 seq, guint8 *data)
 {
-	qq_send_cmd(gc, QQ_CMD_RECV_IM, FALSE, seq, FALSE, data, 16);
+	qq_data *qd;
+
+	qd = (qq_data *) gc->proto_data;
+	qq_send_cmd_detail(qd, QQ_CMD_RECV_IM, seq, FALSE, data, 16);
 }
 
 /* read the common parts of the normal_im,
  * returns the bytes read if succeed, or -1 if there is any error */
-static gint _qq_normal_im_common_read(guint8 *data, guint8 **cursor, gint len, qq_recv_normal_im_common *common)
+static gint _qq_normal_im_common_read(guint8 *data, gint len, qq_recv_normal_im_common *common)
 {
 	gint bytes;
 	g_return_val_if_fail(data != NULL && len != 0 && common != NULL, -1);
 
 	bytes = 0;
 	/* now push data into common header */
-	bytes += read_packet_w(data, cursor, len, &(common->sender_ver));
-	bytes += read_packet_dw(data, cursor, len, &(common->sender_uid));
-	bytes += read_packet_dw(data, cursor, len, &(common->receiver_uid));
+	bytes += qq_get16(&(common->sender_ver), data + bytes);
+	bytes += qq_get32(&(common->sender_uid), data + bytes);
+	bytes += qq_get32(&(common->receiver_uid), data + bytes);
 
-	common->session_md5 = g_memdup(*cursor, QQ_KEY_LENGTH);
+	common->session_md5 = g_memdup(data + bytes, QQ_KEY_LENGTH);
 	bytes += QQ_KEY_LENGTH;
-	*cursor += QQ_KEY_LENGTH;
 
-	bytes += read_packet_w(data, cursor, len, &(common->normal_im_type));
+	bytes += qq_get16(&(common->normal_im_type), data + bytes);
 
 	if (bytes != 28) {	/* read common place fail */
 		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Expect 28 bytes, read %d bytes\n", bytes);
@@ -253,8 +252,7 @@
 }
 
 /* process received normal text IM */
-static void _qq_process_recv_normal_im_text
-    (guint8 *data, guint8 **cursor, gint len, qq_recv_normal_im_common *common, PurpleConnection *gc)
+static void _qq_process_recv_normal_im_text(guint8 *data, gint len, qq_recv_normal_im_common *common, PurpleConnection *gc)
 {
 	guint16 purple_msg_type;
 	gchar *name;
@@ -262,50 +260,52 @@
 	gchar *msg_utf8_encoded;
 	qq_data *qd;
 	qq_recv_normal_im_text *im_text;
+	gint bytes = 0;
 
 	g_return_if_fail(common != NULL);
 	qd = (qq_data *) gc->proto_data;
 
 	/* now it is QQ_NORMAL_IM_TEXT */
-	if (*cursor >= (data + len - 1)) {
-		purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Received normal IM text is empty\n");
-		return;
-	} else
-		im_text = g_newa(qq_recv_normal_im_text, 1);
+	/*
+	   if (*cursor >= (data + len - 1)) {
+	   purple_debug(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;
 
 	/* push data into im_text */
-	read_packet_w(data, cursor, len, &(im_text->msg_seq));
-	read_packet_dw(data, cursor, len, &(im_text->send_time));
-	read_packet_w(data, cursor, len, &(im_text->sender_icon));
-	read_packet_data(data, cursor, len, (guint8 *) & (im_text->unknown2), 3);
-	read_packet_b(data, cursor, len, &(im_text->is_there_font_attr));
+	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();
+	 *	fragmentSequence = buf.get() & 255;
+	 *	messageId = buf.getChar();
 	 */
-	read_packet_data(data, cursor, len, (guint8 *) & (im_text->unknown3), 4);
-	read_packet_b(data, cursor, len, &(im_text->msg_type));
+	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 **) cursor, data + len - *cursor);
+		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 **) cursor);
-			*cursor += strlen(im_text->msg) + 1;
-			im_text->font_attr_len = data + len - *cursor;
-			im_text->font_attr = g_memdup(*cursor, im_text->font_attr_len);
+			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 **) cursor, data + len - *cursor);
+			im_text->msg = g_strndup((gchar *)(data + bytes), len - bytes);
 	}			/* if im_text->msg_type */
-	_qq_show_packet("QQ_MESG recv", data, *cursor - data);
 
 	name = uid_to_purple_name(common->sender_uid);
 	if (purple_find_buddy(gc->account, name) == NULL)
@@ -315,9 +315,9 @@
 
 	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_to_utf8(msg_with_purple_smiley, QQ_CHARSET_DEFAULT);
+		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);
 
 	/* send encoded to purple, note that we use im_text->send_time,
 	 * not the time we receive the message
@@ -333,81 +333,68 @@
 }
 
 /* it is a normal IM, maybe text or video request */
-static void _qq_process_recv_normal_im(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc)
+static void _qq_process_recv_normal_im(guint8 *data, gint len, PurpleConnection *gc)
 {
-	gint bytes;
+	gint bytes = 0;
 	qq_recv_normal_im_common *common;
 	qq_recv_normal_im_unprocessed *im_unprocessed;
-	gchar *hex_dump;
 
 	g_return_if_fail (data != NULL && len != 0);
 
-	if (*cursor >= (data + len - 1)) {
-		purple_debug (PURPLE_DEBUG_WARNING, "QQ",
-			    "Received normal IM is empty\n");
-		return;
-	}
-	else
-		common = g_newa (qq_recv_normal_im_common, 1);
+	common = g_newa (qq_recv_normal_im_common, 1);
 
-	bytes = _qq_normal_im_common_read (data, cursor, len, common);
+	bytes = _qq_normal_im_common_read(data, len, common);
 	if (bytes < 0) {
 		purple_debug (PURPLE_DEBUG_ERROR, "QQ",
-			    "Fail read the common part of normal IM\n");
+				"Fail read the common part of normal IM\n");
 		return;
 	}
 
 	switch (common->normal_im_type) {
-	case QQ_NORMAL_IM_TEXT:
-		purple_debug (PURPLE_DEBUG_INFO,
-			    "QQ",
-			    "Normal IM, text type:\n [%d] => [%d], src: %s\n",
-			    common->sender_uid, common->receiver_uid,
-			    qq_get_source_str (common->sender_ver));
-		_qq_process_recv_normal_im_text (data, cursor, len, common,
-						 gc);
-		break;
-	case QQ_NORMAL_IM_FILE_REJECT_UDP:
-		qq_process_recv_file_reject (data, cursor, len,
-					     common->sender_uid, gc);
-		break;
-	case QQ_NORMAL_IM_FILE_APPROVE_UDP:
-		qq_process_recv_file_accept (data, cursor, len,
-					     common->sender_uid, gc);
-		break;
-	case QQ_NORMAL_IM_FILE_REQUEST_UDP:
-		qq_process_recv_file_request (data, cursor, len,
-					      common->sender_uid, gc);
-		break;
-	case QQ_NORMAL_IM_FILE_CANCEL:
-		qq_process_recv_file_cancel (data, cursor, len,
-					     common->sender_uid, gc);
-		break;
-	case QQ_NORMAL_IM_FILE_NOTIFY:
-		qq_process_recv_file_notify (data, cursor, len,
-				common->sender_uid, gc);
-		break;
-	default:
-		im_unprocessed = g_newa (qq_recv_normal_im_unprocessed, 1);
-		im_unprocessed->common = common;
-		im_unprocessed->unknown = *cursor;
-		im_unprocessed->length = data + len - *cursor;
-		/* a simple process here, maybe more later */
-		purple_debug (PURPLE_DEBUG_WARNING, "QQ",
-			    "Normal IM, unprocessed type [0x%04x]\n",
-			    common->normal_im_type);
-	       	hex_dump = hex_dump_to_str(im_unprocessed->unknown, im_unprocessed->length);
-		purple_debug (PURPLE_DEBUG_WARNING, "QQ", "Dump unknown part.\n%s", hex_dump);
-		g_free(hex_dump);
-		g_free (common->session_md5);
-		return;
+		case QQ_NORMAL_IM_TEXT:
+			purple_debug (PURPLE_DEBUG_INFO, "QQ",
+					"Normal IM, text type:\n [%d] => [%d], src: %s\n",
+					common->sender_uid, common->receiver_uid,
+					qq_get_source_str (common->sender_ver));
+			if (bytes >= len - 1) {
+				purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Received normal IM text is empty\n");
+				return;
+			}
+			_qq_process_recv_normal_im_text(data + bytes, len - bytes, common, gc);
+			break;
+		case QQ_NORMAL_IM_FILE_REJECT_UDP:
+			qq_process_recv_file_reject(data + bytes, len - bytes, common->sender_uid, gc);
+			break;
+		case QQ_NORMAL_IM_FILE_APPROVE_UDP:
+			qq_process_recv_file_accept(data + bytes, len - bytes, common->sender_uid, gc);
+			break;
+		case QQ_NORMAL_IM_FILE_REQUEST_UDP:
+			qq_process_recv_file_request(data + bytes, len - bytes, common->sender_uid, gc);
+			break;
+		case QQ_NORMAL_IM_FILE_CANCEL:
+			qq_process_recv_file_cancel(data + bytes, len - bytes, common->sender_uid, gc);
+			break;
+		case QQ_NORMAL_IM_FILE_NOTIFY:
+			qq_process_recv_file_notify(data + bytes, len - bytes, common->sender_uid, gc);
+			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 (PURPLE_DEBUG_WARNING, "QQ",
+					"Normal IM, unprocessed type [0x%04x], unknown [0x%02x], len %d\n",
+					common->normal_im_type, im_unprocessed->unknown, im_unprocessed->length);
+			g_free (common->session_md5);
+			return;
 	}
 
 	g_free (common->session_md5);
 }
 
 /* process im from system administrator */
-static void _qq_process_recv_sys_im(guint8 *data, guint8 **cursor, gint data_len, PurpleConnection *gc)
+static void _qq_process_recv_sys_im(guint8 *data, gint data_len, PurpleConnection *gc)
 {
 	gint len;
 	guint8 reply;
@@ -415,14 +402,9 @@
 
 	g_return_if_fail(data != NULL && data_len != 0);
 
-	if (*cursor >= (data + data_len - 1)) {
-		purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Received sys IM is empty\n");
-		return;
-	}
+	len = data_len;
 
-	len = data + data_len - *cursor;
-
-	if (NULL == (segments = split_data(*cursor, len, "\x2f", 2)))
+	if (NULL == (segments = split_data(data, len, "\x2f", 2)))
 		return;
 
 	reply = strtol(segments[0], NULL, 10);
@@ -436,7 +418,7 @@
 void qq_send_packet_im(PurpleConnection *gc, guint32 to_uid, gchar *msg, gint type)
 {
 	qq_data *qd;
-	guint8 *cursor, *raw_data, *send_im_tail;
+	guint8 *raw_data, *send_im_tail;
 	guint16 client_tag, normal_im_type;
 	gint msg_len, raw_len, font_name_len, tail_len, bytes;
 	time_t now;
@@ -500,52 +482,51 @@
 
 	raw_len = QQ_SEND_IM_BEFORE_MSG_LEN + msg_len + tail_len;
 	raw_data = g_newa(guint8, raw_len);
-	cursor = raw_data;
 	bytes = 0;
 
 	/* 000-003: receiver uid */
-	bytes += create_packet_dw(raw_data, &cursor, qd->uid);
+	bytes += qq_put32(raw_data + bytes, qd->uid);
 	/* 004-007: sender uid */
-	bytes += create_packet_dw(raw_data, &cursor, to_uid);
+	bytes += qq_put32(raw_data + bytes, to_uid);
 	/* 008-009: sender client version */
-	bytes += create_packet_w(raw_data, &cursor, client_tag);
+	bytes += qq_put16(raw_data + bytes, client_tag);
 	/* 010-013: receiver uid */
-	bytes += create_packet_dw(raw_data, &cursor, qd->uid);
+	bytes += qq_put32(raw_data + bytes, qd->uid);
 	/* 014-017: sender uid */
-	bytes += create_packet_dw(raw_data, &cursor, to_uid);
+	bytes += qq_put32(raw_data + bytes, to_uid);
 	/* 018-033: md5 of (uid+session_key) */
-	bytes += create_packet_data(raw_data, &cursor, qd->session_md5, 16);
+	bytes += qq_putdata(raw_data + bytes, qd->session_md5, 16);
 	/* 034-035: message type */
-	bytes += create_packet_w(raw_data, &cursor, normal_im_type);
+	bytes += qq_put16(raw_data + bytes, normal_im_type);
 	/* 036-037: sequence number */
-	bytes += create_packet_w(raw_data, &cursor, qd->send_seq);
+	bytes += qq_put16(raw_data + bytes, qd->send_seq);
 	/* 038-041: send time */
-	bytes += create_packet_dw(raw_data, &cursor, (guint32) now);
+	bytes += qq_put32(raw_data + bytes, (guint32) now);
 	/* 042-043: sender icon */
-	bytes += create_packet_w(raw_data, &cursor, qd->my_icon);
+	bytes += qq_put16(raw_data + bytes, qd->my_icon);
 	/* 044-046: always 0x00 */
-	bytes += create_packet_w(raw_data, &cursor, 0x0000);
-	bytes += create_packet_b(raw_data, &cursor, 0x00);
+	bytes += qq_put16(raw_data + bytes, 0x0000);
+	bytes += qq_put8(raw_data + bytes, 0x00);
 	/* 047-047: we use font attr */
-	bytes += create_packet_b(raw_data, &cursor, 0x01);
+	bytes += qq_put8(raw_data + bytes, 0x01);
 	/* 048-051: always 0x00 */
-	bytes += create_packet_dw(raw_data, &cursor, 0x00000000);
+	bytes += qq_put32(raw_data + bytes, 0x00000000);
 	/* 052-052: text message type (normal/auto-reply) */
-	bytes += create_packet_b(raw_data, &cursor, type);
+	bytes += qq_put8(raw_data + bytes, type);
 	/* 053-   : msg ends with 0x00 */
-	bytes += create_packet_data(raw_data, &cursor, (guint8 *) msg_filtered, msg_len);
+	bytes += qq_putdata(raw_data + bytes, (guint8 *) msg_filtered, msg_len);
 	send_im_tail = qq_get_send_im_tail(font_color, font_size, font_name, is_bold,
-						   is_italic, is_underline, tail_len);
-	_qq_show_packet("QQ_MESG debug", send_im_tail, tail_len);
-	bytes += create_packet_data(raw_data, &cursor, send_im_tail, tail_len);
+			is_italic, is_underline, tail_len);
+	qq_show_packet("QQ_send_im_tail debug", send_im_tail, tail_len);
+	bytes += qq_putdata(raw_data + bytes, send_im_tail, tail_len);
 
-	_qq_show_packet("QQ_MESG raw", raw_data, cursor - raw_data);
+	qq_show_packet("QQ_raw_data debug", raw_data, bytes);
 
 	if (bytes == raw_len)	/* create packet OK */
-		qq_send_cmd(gc, QQ_CMD_SEND_IM, TRUE, 0, TRUE, raw_data, cursor - raw_data);
+		qq_send_cmd(qd, QQ_CMD_SEND_IM, raw_data, bytes);
 	else
 		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
-			   "Fail creating send_im packet, expect %d bytes, build %d bytes\n", raw_len, bytes);
+				"Fail creating send_im packet, expect %d bytes, build %d bytes\n", raw_len, bytes);
 
 	if (font_color)
 		g_free(font_color);
@@ -560,7 +541,8 @@
 {
 	qq_data *qd;
 	gint len;
-	guint8 *data, *cursor, reply;
+	guint8 *data, reply;
+	gint bytes = 0;
 
 	g_return_if_fail(buf != NULL && buf_len != 0);
 
@@ -569,8 +551,7 @@
 	data = g_newa(guint8, len);
 
 	if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
-		cursor = data;
-		read_packet_b(data, &cursor, len, &reply);
+		bytes += qq_get8(&reply, data + bytes);
 		if (reply != QQ_SEND_IM_REPLY_OK) {
 			purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Send IM fail\n");
 			purple_notify_error(gc, _("Error"), _("Failed to send IM."), NULL);
@@ -588,7 +569,7 @@
 {
 	qq_data *qd;
 	gint len, bytes;
-	guint8 *data, *cursor;
+	guint8 *data;
 	qq_recv_im_header *im_header;
 
 	g_return_if_fail(buf != NULL && buf_len != 0);
@@ -597,98 +578,107 @@
 	len = buf_len;
 	data = g_newa(guint8, len);
 
-	if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
-		if (len < 16) {	/* we need to ack with the first 16 bytes */
-			purple_debug(PURPLE_DEBUG_ERROR, "QQ", "IM is too short\n");
-			return;
-		} else
-			_qq_send_packet_recv_im_ack(gc, seq, data);
+	if (!qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt rev im\n");
+	}
+
+	if (len < 16) {	/* we need to ack with the first 16 bytes */
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "IM is too short\n");
+		return;
+	} else {
+		_qq_send_packet_recv_im_ack(gc, seq, data);
+	}
+
+	/* check len first */
+	if (len < 20) {	/* length of im_header */
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
+				"Fail read recv IM header, len should longer than 20 bytes, read %d bytes\n", len);
+		return;
+	}
 
-		cursor = data;
-		bytes = 0;
-		im_header = g_newa(qq_recv_im_header, 1);
-		bytes += read_packet_dw(data, &cursor, len, &(im_header->sender_uid));
-		bytes += read_packet_dw(data, &cursor, len, &(im_header->receiver_uid));
-		bytes += read_packet_dw(data, &cursor, len, &(im_header->server_im_seq));
-		/* if the message is delivered via server, it is server IP/port */
-		bytes += read_packet_data(data, &cursor, len, (guint8 *) & (im_header->sender_ip), 4);
-		bytes += read_packet_w(data, &cursor, len, &(im_header->sender_port));
-		bytes += read_packet_w(data, &cursor, len, &(im_header->im_type));
+	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_getdata((guint8 *) & (im_header->sender_ip), 4, data + bytes);
+	bytes += qq_get16(&(im_header->sender_port), data + bytes);
+	bytes += qq_get16(&(im_header->im_type), data + bytes);
+	/* im_header prepared */
 
-		if (bytes != 20) {	/* length of im_header */
-			purple_debug(PURPLE_DEBUG_ERROR, "QQ",
-				   "Fail read recv IM header, expect 20 bytes, read %d bytes\n", bytes);
-			return;
-		}
+	if (im_header->receiver_uid != qd->uid) {	/* should not happen */
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "IM to [%d], NOT me\n", im_header->receiver_uid);
+		return;
+	}
 
-		if (im_header->receiver_uid != qd->uid) {	/* should not happen */
-			purple_debug(PURPLE_DEBUG_ERROR, "QQ", "IM to [%d], NOT me\n", im_header->receiver_uid);
-			return;
-		}
+	/* check bytes */
+	if (bytes >= len - 1) {
+		purple_debug (PURPLE_DEBUG_WARNING, "QQ", "Received IM is empty\n");
+		return;
+	}
 
-		switch (im_header->im_type) {
+	switch (im_header->im_type) {
 		case QQ_RECV_IM_TO_BUDDY:
 			purple_debug(PURPLE_DEBUG_INFO, "QQ",
-				   "IM from buddy [%d], I am in his/her buddy list\n", im_header->sender_uid);
-			_qq_process_recv_normal_im(data, &cursor, len, gc);
+					"IM from buddy [%d], I am in his/her buddy list\n", im_header->sender_uid);
+			_qq_process_recv_normal_im(data + bytes, len - bytes, gc); /* position and rest length */
 			break;
 		case QQ_RECV_IM_TO_UNKNOWN:
 			purple_debug(PURPLE_DEBUG_INFO, "QQ",
-				   "IM from buddy [%d], I am a stranger to him/her\n", im_header->sender_uid);
-			_qq_process_recv_normal_im(data, &cursor, len, gc);
+					"IM from buddy [%d], I am a stranger to him/her\n", im_header->sender_uid);
+			_qq_process_recv_normal_im(data + bytes, 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(PURPLE_DEBUG_INFO, "QQ", "IM from group, internal_id [%d]\n", im_header->sender_uid);
 			/* sender_uid is in fact internal_group_id */
-			qq_process_recv_group_im(data, &cursor, len, im_header->sender_uid, gc, im_header->im_type);
+			qq_process_recv_group_im(data + bytes, len - bytes, im_header->sender_uid, gc, im_header->im_type);
 			break;
 		case QQ_RECV_IM_ADD_TO_QUN:
 			purple_debug(PURPLE_DEBUG_INFO, "QQ",
-				   "IM from group, added by group internal_id [%d]\n", im_header->sender_uid);
+					"IM from group, added by group internal_id [%d]\n", im_header->sender_uid);
 			/* sender_uid is in fact internal_group_id
 			 * we need this to create a dummy group and add to blist */
-			qq_process_recv_group_im_been_added(data, &cursor, len, im_header->sender_uid, gc);
+			qq_process_recv_group_im_been_added(data + bytes, len - bytes, im_header->sender_uid, gc);
 			break;
 		case QQ_RECV_IM_DEL_FROM_QUN:
 			purple_debug(PURPLE_DEBUG_INFO, "QQ",
-				   "IM from group, removed by group internal_ID [%d]\n", im_header->sender_uid);
+					"IM from group, removed by group internal_ID [%d]\n", im_header->sender_uid);
 			/* sender_uid is in fact internal_group_id */
-			qq_process_recv_group_im_been_removed(data, &cursor, len, im_header->sender_uid, gc);
+			qq_process_recv_group_im_been_removed(data + bytes, len - bytes, im_header->sender_uid, gc);
 			break;
 		case QQ_RECV_IM_APPLY_ADD_TO_QUN:
 			purple_debug(PURPLE_DEBUG_INFO, "QQ",
-				   "IM from group, apply to join group internal_ID [%d]\n", im_header->sender_uid);
+					"IM from group, apply to join group internal_ID [%d]\n", im_header->sender_uid);
 			/* sender_uid is in fact internal_group_id */
-			qq_process_recv_group_im_apply_join(data, &cursor, len, im_header->sender_uid, gc);
+			qq_process_recv_group_im_apply_join(data + bytes, len - bytes, im_header->sender_uid, gc);
 			break;
 		case QQ_RECV_IM_APPROVE_APPLY_ADD_TO_QUN:
 			purple_debug(PURPLE_DEBUG_INFO, "QQ",
-				   "IM for group system info, approved by group internal_id [%d]\n",
-				   im_header->sender_uid);
+					"IM for group system info, approved by group internal_id [%d]\n",
+					im_header->sender_uid);
 			/* sender_uid is in fact internal_group_id */
-			qq_process_recv_group_im_been_approved(data, &cursor, len, im_header->sender_uid, gc);
+			qq_process_recv_group_im_been_approved(data + bytes, len - bytes, im_header->sender_uid, gc);
 			break;
 		case QQ_RECV_IM_REJCT_APPLY_ADD_TO_QUN:
 			purple_debug(PURPLE_DEBUG_INFO, "QQ",
-				   "IM for group system info, rejected by group internal_id [%d]\n",
-				   im_header->sender_uid);
+					"IM for group system info, rejected by group internal_id [%d]\n",
+					im_header->sender_uid);
 			/* sender_uid is in fact internal_group_id */
-			qq_process_recv_group_im_been_rejected(data, &cursor, len, im_header->sender_uid, gc);
+			qq_process_recv_group_im_been_rejected(data + bytes, len - bytes, im_header->sender_uid, gc);
 			break;
 		case QQ_RECV_IM_SYS_NOTIFICATION:
 			purple_debug(PURPLE_DEBUG_INFO, "QQ",
-				   "IM from [%d], should be a system administrator\n", im_header->sender_uid);
-			_qq_process_recv_sys_im(data, &cursor, len, gc);
+					"IM from [%d], should be a system administrator\n", im_header->sender_uid);
+			_qq_process_recv_sys_im(data + bytes, len - bytes, gc);
 			break;
 		default:
 			purple_debug(PURPLE_DEBUG_WARNING, "QQ",
-				   "IM from [%d], [0x%02x] %s is not processed\n",
-				   im_header->sender_uid,
-				   im_header->im_type, qq_get_recv_im_type_str(im_header->im_type));
-		}
-	} else {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt rev im\n");
+					"IM from [%d], [0x%02x] %s is not processed\n",
+					im_header->sender_uid,
+					im_header->im_type, qq_get_recv_im_type_str(im_header->im_type));
 	}
 }
+
--- a/libpurple/protocols/qq/keep_alive.c	Wed Jul 09 00:27:44 2008 +0000
+++ b/libpurple/protocols/qq/keep_alive.c	Wed Jul 09 00:32:18 2008 +0000
@@ -40,7 +40,7 @@
 #include "header_info.h"
 #include "keep_alive.h"
 #include "packet_parse.h"
-#include "send_core.h"
+#include "qq_network.h"
 #include "utils.h"
 
 #define QQ_UPDATE_ONLINE_INTERVAL   300	/* in sec */
@@ -49,18 +49,17 @@
 void qq_send_packet_keep_alive(PurpleConnection *gc)
 {
 	qq_data *qd;
-	guint8 *raw_data, *cursor;
+	guint8 raw_data[16] = {0};
+	gint bytes= 0;
 
 	qd = (qq_data *) gc->proto_data;
-	raw_data = g_newa(guint8, 4);
-	cursor = raw_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 */
-	create_packet_dw(raw_data, &cursor, qd->uid);
+	bytes += qq_put32(raw_data + bytes, qd->uid);
 
-	qq_send_cmd(gc, QQ_CMD_KEEP_ALIVE, TRUE, 0, TRUE, raw_data, 4);
+	qq_send_cmd(qd, QQ_CMD_KEEP_ALIVE, raw_data, 4);
 }
 
 /* parse the return of keep-alive packet, it includes some system information */
--- a/libpurple/protocols/qq/login_logout.c	Wed Jul 09 00:27:44 2008 +0000
+++ b/libpurple/protocols/qq/login_logout.c	Wed Jul 09 00:32:18 2008 +0000
@@ -25,6 +25,7 @@
 #include "debug.h"
 #include "internal.h"
 #include "server.h"
+#include "cipher.h"
 
 #include "buddy_info.h"
 #include "buddy_list.h"
@@ -36,8 +37,7 @@
 #include "login_logout.h"
 #include "packet_parse.h"
 #include "qq.h"
-#include "qq_proxy.h"
-#include "send_core.h"
+#include "qq_network.h"
 #include "utils.h"
 
 #define QQ_LOGIN_DATA_LENGTH		    416
@@ -70,32 +70,36 @@
 */
 
 /* for QQ 2005? copy from lumaqq */
-static const gint8 login_23_51[29] = {
-	0, 0, 0, 
-	0, 0, 0, 0, 0, 0, 0, 0, 0, -122, 
-	-52, 76, 53, 44, -45, 115, 108, 20, -10, -10, 
-	-81, -61, -6, 51, -92, 1
+/* FIXME: change to guint8 */
+static const guint8 login_23_51[29] = {
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x86, 0xcc, 0x4c, 0x35,
+	0x2c, 0xd3, 0x73, 0x6c, 0x14, 0xf6, 0xf6, 0xaf,
+	0xc3, 0xfa, 0x33, 0xa4, 0x01
 };
 
-static const gint8 login_53_68[16] = {
-	-115, -117, -6, -20, -43, 82, 23, 74, -122, -7, 
-	-89, 117, -26, 50, -47, 109
+static const guint8 login_53_68[16] = {
+ 	0x8D, 0x8B, 0xFA, 0xEC, 0xD5, 0x52, 0x17, 0x4A,
+ 	0x86, 0xF9, 0xA7, 0x75, 0xE6, 0x32, 0xD1, 0x6D
 };
 
-static const gint8 login_100_bytes[100] = {
-	64, 
-	11, 4, 2, 0, 1, 0, 0, 0, 0, 0, 
-	3, 9, 0, 0, 0, 0, 0, 0, 0, 1, 
-	-23, 3, 1, 0, 0, 0, 0, 0, 1, -13, 
-	3, 0, 0, 0, 0, 0, 0, 1, -19, 3, 
-	0, 0, 0, 0, 0, 0, 1, -20, 3, 0, 
-	0, 0, 0, 0, 0, 3, 5, 0, 0, 0, 
-	0, 0, 0, 0, 3, 7, 0, 0, 0, 0, 
-	0, 0, 0, 1, -18, 3, 0, 0, 0, 0, 
-	0, 0, 1, -17, 3, 0, 0, 0, 0, 0, 
-	0, 1, -21, 3, 0, 0, 0, 0, 0
+static const guint8 login_100_bytes[100] = {
+	0x40, 0x0B, 0x04, 0x02, 0x00, 0x01, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x03, 0x09, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x01, 0xE9, 0x03, 0x01,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xF3, 0x03,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xED,
+	0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+	0xEC, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x03, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x03, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x01, 0xEE, 0x03, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x01, 0xEF, 0x03, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x01, 0xEB, 0x03, 0x00,
+	0x00, 0x00, 0x00, 0x00
 };
 
+
 /* fixed value, not affected by version, or mac address */
 /*
 static const guint8 login_53_68[16] = {
@@ -138,74 +142,80 @@
 	guint16 new_server_port;
 };
 
-extern gint			/* defined in send_core.c */
- _create_packet_head_seq(guint8 *buf,
-			 guint8 **cursor, PurpleConnection *gc, guint16 cmd, gboolean is_auto_seq, guint16 *seq);
-extern gint			/* defined in send_core.c */
- _qq_send_packet(PurpleConnection *gc, guint8 *buf, gint len, guint16 cmd);
+/* generate a md5 key using uid and session_key */
+static guint8 *gen_session_md5(gint uid, guint8 *session_key)
+{
+	guint8 *src, md5_str[QQ_KEY_LENGTH];
+	PurpleCipher *cipher;
+	PurpleCipherContext *context;
 
-/* It is fixed to 16 bytes 0x01 for QQ2003, 
- * Any value works (or a random 16 bytes string) */
-static guint8 *_gen_login_key(void)
-{
-	return (guint8 *) g_strnfill(QQ_KEY_LENGTH, 0x01);
+	src = g_newa(guint8, 20);
+	/* bug found by QuLogic */
+	memcpy(src, &uid, sizeof(uid));
+	memcpy(src + sizeof(uid), session_key, QQ_KEY_LENGTH);
+
+	cipher = purple_ciphers_find_cipher("md5");
+	context = purple_cipher_context_new(cipher, NULL);
+	purple_cipher_context_append(context, src, 20);
+	purple_cipher_context_digest(context, sizeof(md5_str), md5_str, NULL);
+	purple_cipher_context_destroy(context);
+
+	return g_memdup(md5_str, QQ_KEY_LENGTH);
 }
 
 /* process login reply which says OK */
 static gint _qq_process_login_ok(PurpleConnection *gc, guint8 *data, gint len)
 {
 	gint bytes;
-	guint8 *cursor;
 	qq_data *qd;
 	qq_login_reply_ok_packet lrop;
 
 	qd = (qq_data *) gc->proto_data;
-	cursor = data;
+	/* FIXME, check QQ_LOGIN_REPLY_OK_PACKET_LEN here */
 	bytes = 0;
 
 	/* 000-000: reply code */
-	bytes += read_packet_b(data, &cursor, len, &lrop.result);
+	bytes += qq_get8(&lrop.result, data + bytes);
 	/* 001-016: session key */
-	lrop.session_key = g_memdup(cursor, QQ_KEY_LENGTH);
-	cursor += QQ_KEY_LENGTH;
+	lrop.session_key = g_memdup(data + bytes, QQ_KEY_LENGTH);
 	bytes += QQ_KEY_LENGTH;
 	purple_debug(PURPLE_DEBUG_INFO, "QQ", "Get session_key done\n");
 	/* 017-020: login uid */
-	bytes += read_packet_dw(data, &cursor, len, &lrop.uid);
+	bytes += qq_get32(&lrop.uid, data + bytes);
 	/* 021-024: server detected user public IP */
-	bytes += read_packet_data(data, &cursor, len, (guint8 *) &lrop.client_ip, 4);
+	bytes += qq_getdata((guint8 *) &lrop.client_ip, 4, data + bytes);
 	/* 025-026: server detected user port */
-	bytes += read_packet_w(data, &cursor, len, &lrop.client_port);
+	bytes += qq_get16(&lrop.client_port, data + bytes);
 	/* 027-030: server detected itself ip 127.0.0.1 ? */
-	bytes += read_packet_data(data, &cursor, len, (guint8 *) &lrop.server_ip, 4);
+	bytes += qq_getdata((guint8 *) &lrop.server_ip, 4, data + bytes);
 	/* 031-032: server listening port */
-	bytes += read_packet_w(data, &cursor, len, &lrop.server_port);
+	bytes += qq_get16(&lrop.server_port, data + bytes);
 	/* 033-036: login time for current session */
-	bytes += read_packet_time(data, &cursor, len, &lrop.login_time);
+	bytes += qq_getime(&lrop.login_time, data + bytes);
 	/* 037-062: 26 bytes, unknown */
-	bytes += read_packet_data(data, &cursor, len, (guint8 *) &lrop.unknown1, 26);
+	bytes += qq_getdata((guint8 *) &lrop.unknown1, 26, data + bytes);
 	/* 063-066: unknown server1 ip address */
-	bytes += read_packet_data(data, &cursor, len, (guint8 *) &lrop.unknown_server1_ip, 4);
+	bytes += qq_getdata((guint8 *) &lrop.unknown_server1_ip, 4, data + bytes);
 	/* 067-068: unknown server1 port */
-	bytes += read_packet_w(data, &cursor, len, &lrop.unknown_server1_port);
+	bytes += qq_get16(&lrop.unknown_server1_port, data + bytes);
 	/* 069-072: unknown server2 ip address */
-	bytes += read_packet_data(data, &cursor, len, (guint8 *) &lrop.unknown_server2_ip, 4);
+	bytes += qq_getdata((guint8 *) &lrop.unknown_server2_ip, 4, data + bytes);
 	/* 073-074: unknown server2 port */
-	bytes += read_packet_w(data, &cursor, len, &lrop.unknown_server2_port);
+	bytes += qq_get16(&lrop.unknown_server2_port, data + bytes);
 	/* 075-076: 2 bytes unknown */
-	bytes += read_packet_w(data, &cursor, len, &lrop.unknown2);
+	bytes += qq_get16(&lrop.unknown2, data + bytes);
 	/* 077-078: 2 bytes unknown */
-	bytes += read_packet_w(data, &cursor, len, &lrop.unknown3);
+	bytes += qq_get16(&lrop.unknown3, data + bytes);
 	/* 079-110: 32 bytes unknown */
-	bytes += read_packet_data(data, &cursor, len, (guint8 *) &lrop.unknown4, 32);
+	bytes += qq_getdata((guint8 *) &lrop.unknown4, 32, data + bytes);
 	/* 111-122: 12 bytes unknown */
-	bytes += read_packet_data(data, &cursor, len, (guint8 *) &lrop.unknown5, 12);
+	bytes += qq_getdata((guint8 *) &lrop.unknown5, 12, data + bytes);
 	/* 123-126: login IP of last session */
-	bytes += read_packet_data(data, &cursor, len, (guint8 *) &lrop.last_client_ip, 4);
+	bytes += qq_getdata((guint8 *) &lrop.last_client_ip, 4, data + bytes);
 	/* 127-130: login time of last session */
-	bytes += read_packet_time(data, &cursor, len, &lrop.last_login_time);
+	bytes += qq_getime(&lrop.last_login_time, data + bytes);
 	/* 131-138: 8 bytes unknown */
-	bytes += read_packet_data(data, &cursor, len, (guint8 *) &lrop.unknown6, 8);
+	bytes += qq_getdata((guint8 *) &lrop.unknown6, 8, data + bytes);
 
 	if (bytes != QQ_LOGIN_REPLY_OK_PACKET_LEN) {	/* fail parsing login info */
 		purple_debug(PURPLE_DEBUG_WARNING, "QQ",
@@ -213,9 +223,15 @@
 			   QQ_LOGIN_REPLY_OK_PACKET_LEN, bytes);
 	}			/* but we still go on as login OK */
 
+	g_return_val_if_fail(qd->session_key == NULL, QQ_LOGIN_REPLY_MISC_ERROR);
 	qd->session_key = lrop.session_key;
-	qd->session_md5 = _gen_session_md5(qd->uid, qd->session_key);
+	
+	g_return_val_if_fail(qd->session_md5 == NULL, QQ_LOGIN_REPLY_MISC_ERROR);
+	qd->session_md5 = gen_session_md5(qd->uid, qd->session_key);
+	
+	g_return_val_if_fail(qd->my_ip == NULL, QQ_LOGIN_REPLY_MISC_ERROR);
 	qd->my_ip = gen_ip_str(lrop.client_ip);
+	
 	qd->my_port = lrop.client_port;
 	qd->login_time = lrop.login_time;
 	qd->last_login_time = lrop.last_login_time;
@@ -247,34 +263,40 @@
 static gint _qq_process_login_redirect(PurpleConnection *gc, guint8 *data, gint len)
 {
 	gint bytes, ret;
-	guint8 *cursor;
-	gchar *new_server_str;
 	qq_data *qd;
 	qq_login_reply_redirect_packet lrrp;
 
 	qd = (qq_data *) gc->proto_data;
-	cursor = data;
 	bytes = 0;
 	/* 000-000: reply code */
-	bytes += read_packet_b(data, &cursor, len, &lrrp.result);
+	bytes += qq_get8(&lrrp.result, data + bytes);
 	/* 001-004: login uid */
-	bytes += read_packet_dw(data, &cursor, len, &lrrp.uid);
+	bytes += qq_get32(&lrrp.uid, data + bytes);
 	/* 005-008: redirected new server IP */
-	bytes += read_packet_data(data, &cursor, len, lrrp.new_server_ip, 4);
+	bytes += qq_getdata(lrrp.new_server_ip, 4, data + bytes);
 	/* 009-010: redirected new server port */
-	bytes += read_packet_w(data, &cursor, len, &lrrp.new_server_port);
+	bytes += qq_get16(&lrrp.new_server_port, data + bytes);
 
 	if (bytes != QQ_LOGIN_REPLY_REDIRECT_PACKET_LEN) {
 		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
 			   "Fail parsing login redirect packet, expect %d bytes, read %d bytes\n",
 			   QQ_LOGIN_REPLY_REDIRECT_PACKET_LEN, bytes);
 		ret = QQ_LOGIN_REPLY_MISC_ERROR;
-	} else {		/* start new connection */
-		new_server_str = gen_ip_str(lrrp.new_server_ip);
+	} else {
+		/* redirect to new server, do not disconnect or connect here
+		 * those connect should be called at packet_process */
+		if (qd->real_hostname) {
+			purple_debug(PURPLE_DEBUG_INFO, "QQ", "free real_hostname\n");
+			g_free(qd->real_hostname);
+			qd->real_hostname = NULL;
+		}
+		qd->real_hostname = gen_ip_str(lrrp.new_server_ip);
+		qd->real_port = lrrp.new_server_port;
+		qd->is_redirect = TRUE;
+
 		purple_debug(PURPLE_DEBUG_WARNING, "QQ",
-			   "Redirected to new server: %s:%d\n", new_server_str, lrrp.new_server_port);
-		qq_connect(gc->account, new_server_str, lrrp.new_server_port, qd->use_tcp, TRUE);
-		g_free(new_server_str);
+			   "Redirected to new server: %s:%d\n", qd->real_hostname, qd->real_port);
+
 		ret = QQ_LOGIN_REPLY_REDIRECT;
 	}
 
@@ -299,89 +321,78 @@
 void qq_send_packet_request_login_token(PurpleConnection *gc)
 {
 	qq_data *qd;
-	guint8 *buf, *cursor;
-	guint16 seq_ret;
-	gint bytes;
-
-	qd = (qq_data *) gc->proto_data;
-	buf = g_newa(guint8, MAX_PACKET_SIZE);
+	guint8 buf[16] = {0};
+	gint bytes = 0;
 
-	cursor = buf;
-	bytes = 0;
-	bytes += _create_packet_head_seq(buf, &cursor, gc, QQ_CMD_REQUEST_LOGIN_TOKEN, TRUE, &seq_ret);
-	bytes += create_packet_dw(buf, &cursor, qd->uid);
-	bytes += create_packet_b(buf, &cursor, 0);
-	bytes += create_packet_b(buf, &cursor, QQ_PACKET_TAIL);
+	g_return_if_fail(gc != NULL && gc->proto_data != NULL);
+	qd = (qq_data *) gc->proto_data;
 
-	if (bytes == (cursor - buf))	/* packet creation OK */
-		_qq_send_packet(gc, buf, bytes, QQ_CMD_REQUEST_LOGIN_TOKEN);
-	else
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Fail create request login token packet\n");
+	bytes += qq_put8(buf + bytes, 0);
+	
+	qq_send_data(qd, QQ_CMD_REQUEST_LOGIN_TOKEN, buf, bytes);
 }
 
 /* send login packet to QQ server */
 static void qq_send_packet_login(PurpleConnection *gc, guint8 token_length, guint8 *token)
 {
 	qq_data *qd;
-	guint8 *buf, *cursor, *raw_data, *encrypted_data;
-	guint16 seq_ret;
-	gint encrypted_len, bytes;
-	gint pos;
+	guint8 *buf, *raw_data;
+	gint bytes;
+	guint8 *encrypted_data;
+	gint encrypted_len;
 
+	g_return_if_fail(gc != NULL && gc->proto_data != NULL);
 	qd = (qq_data *) gc->proto_data;
-	buf = g_newa(guint8, MAX_PACKET_SIZE);
+
 	raw_data = g_newa(guint8, QQ_LOGIN_DATA_LENGTH);
+	memset(raw_data, 0, QQ_LOGIN_DATA_LENGTH);
+
 	encrypted_data = g_newa(guint8, QQ_LOGIN_DATA_LENGTH + 16);	/* 16 bytes more */
-	qd->inikey = _gen_login_key();
+	if (qd->inikey) {
+		g_free(qd->inikey);
+	}
+	qd->inikey = (guint8 *) g_strnfill(QQ_KEY_LENGTH, 0x01);
 
+	bytes = 0;
 	/* now generate the encrypted data
 	 * 000-015 use pwkey as key to encrypt empty string */
-	qq_encrypt((guint8 *) "", 0, qd->pwkey, raw_data, &encrypted_len);
+	qq_encrypt((guint8 *) "", 0, qd->pwkey, raw_data + bytes, &encrypted_len);
+	bytes += 16;
 	/* 016-016 */
-	raw_data[16] = 0x00;
+	bytes += qq_put8(raw_data + bytes, 0x00);
 	/* 017-020, used to be IP, now zero */
-	*((guint32 *) (raw_data + 17)) = 0x00000000;
+	bytes += qq_put32(raw_data + bytes, 0x00000000);
 	/* 021-022, used to be port, now zero */
-	*((guint16 *) (raw_data + 21)) = 0x0000;
+	bytes += qq_put16(raw_data + bytes, 0x0000);
 	/* 023-051, fixed value, unknown */
-	g_memmove(raw_data + 23, login_23_51, 29);
+	bytes += qq_putdata(raw_data + bytes, login_23_51, 29);
 	/* 052-052, login mode */
-	raw_data[52] = qd->login_mode;
+	bytes += qq_put8(raw_data + bytes, qd->login_mode);
 	/* 053-068, fixed value, maybe related to per machine */
-	g_memmove(raw_data + 53, login_53_68, 16);
-
+	bytes += qq_putdata(raw_data + bytes, login_53_68, 16);
 	/* 069, login token length */
-	raw_data[69] = token_length;
-	pos = 70;
+	bytes += qq_put8(raw_data + bytes, token_length);
 	/* 070-093, login token, normally 24 bytes */
-	g_memmove(raw_data + pos, token, token_length);
-	pos += token_length;
+	bytes += qq_putdata(raw_data + bytes, token, token_length);
 	/* 100 bytes unknown */
-	g_memmove(raw_data + pos, login_100_bytes, 100);
-	pos += 100;
+	bytes += qq_putdata(raw_data + bytes, login_100_bytes, 100);
 	/* all zero left */
-	memset(raw_data+pos, 0, QQ_LOGIN_DATA_LENGTH - pos);
 
 	qq_encrypt(raw_data, QQ_LOGIN_DATA_LENGTH, qd->inikey, encrypted_data, &encrypted_len);
 
-	cursor = buf;
+	buf = g_newa(guint8, MAX_PACKET_SIZE);
+	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);
-	bytes += create_packet_data(buf, &cursor, qd->inikey, QQ_KEY_LENGTH);
-	bytes += create_packet_data(buf, &cursor, encrypted_data, encrypted_len);
-	bytes += create_packet_b(buf, &cursor, QQ_PACKET_TAIL);
+	bytes += qq_putdata(buf + bytes, qd->inikey, QQ_KEY_LENGTH);
+	bytes += qq_putdata(buf + bytes, encrypted_data, encrypted_len);
 
-	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");
+	qq_send_data(qd, QQ_CMD_LOGIN, buf, bytes);
 }
 
 void qq_process_request_login_token_reply(guint8 *buf, gint buf_len, PurpleConnection *gc)
 {
 	qq_data *qd;
-	gchar *hex_dump;
+	gchar *error_msg;
 
 	g_return_if_fail(buf != NULL && buf_len != 0);
 
@@ -394,20 +405,24 @@
 			purple_debug(PURPLE_DEBUG_INFO, "QQ",
 					"Attempting to proceed with the actual packet length.\n");
 		}
-		hex_dump = hex_dump_to_str(buf+2, buf_len-2);
-		purple_debug(PURPLE_DEBUG_INFO, "QQ",
-				"<<< got a token with %d bytes -> [default] decrypt and dump\n%s", buf_len-2, hex_dump);
+		qq_hex_dump(PURPLE_DEBUG_INFO, "QQ",
+				buf+2, buf_len-2,
+				"<<< got a token -> [default] decrypt and dump");
 		qq_send_packet_login(gc, buf_len-2, buf+2);
 	} else {
 		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Unknown request login token reply code : %d\n", buf[0]);
-		hex_dump = hex_dump_to_str(buf, buf_len);
-		purple_debug(PURPLE_DEBUG_WARNING, "QQ",
-				">>> %d bytes -> [default] decrypt and dump\n%s",
-				buf_len, hex_dump);
-		try_dump_as_gbk(buf, buf_len);
-		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Error requesting login token"));
+		qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ",
+				buf, buf_len,
+				">>> [default] decrypt and dump");
+		error_msg = try_dump_as_gbk(buf, buf_len);
+		if (error_msg) {
+			purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_msg);
+			g_free(error_msg);
+		} else {
+			purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+				_("Error requesting login token"));
+		}
 	}
-	g_free(hex_dump);
 }
 
 /* send logout packets to QQ server */
@@ -418,7 +433,7 @@
 
 	qd = (qq_data *) gc->proto_data;
 	for (i = 0; i < 4; i++)
-		qq_send_cmd(gc, QQ_CMD_LOGOUT, FALSE, 0xffff, FALSE, qd->pwkey, QQ_KEY_LENGTH);
+		qq_send_cmd_detail(qd, QQ_CMD_LOGOUT, 0xffff, FALSE, qd->pwkey, QQ_KEY_LENGTH);
 
 	qd->logged_in = FALSE;	/* update login status AFTER sending logout packets */
 }
@@ -429,7 +444,7 @@
 	gint len, ret, bytes;
 	guint8 *data;
 	qq_data *qd;
-	gchar *hex_dump;
+	gchar* error_msg;
 
 	g_return_if_fail(buf != NULL && buf_len != 0);
 
@@ -462,13 +477,14 @@
 				break;
 			default:
 				purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Unknown reply code: %d\n", data[0]);
-				hex_dump = hex_dump_to_str(data, len);
-				purple_debug(PURPLE_DEBUG_WARNING, "QQ",
-						">>> %d bytes -> [default] decrypt and dump\n%s",
-						buf_len, hex_dump);
-				g_free(hex_dump);
-				try_dump_as_gbk(data, len);
-
+				qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ",
+						data, len,
+						">>> [default] decrypt and dump");
+				error_msg = try_dump_as_gbk(data, len);
+				if (error_msg)	{
+					purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_msg);
+					g_free(error_msg);
+				}
 				ret = QQ_LOGIN_REPLY_MISC_ERROR;
 			}
 		} else {	/* no idea how to decrypt */
--- a/libpurple/protocols/qq/packet_parse.c	Wed Jul 09 00:27:44 2008 +0000
+++ b/libpurple/protocols/qq/packet_parse.c	Wed Jul 09 00:32:18 2008 +0000
@@ -25,119 +25,147 @@
 #include <string.h>
 
 #include "packet_parse.h"
+#include "debug.h"
+
+
+/*------------------------------------------------PUT------------------------------------------------*/
+
+/* note:
+ * 1, in these functions, 'b' stands for byte, 'w' stands for word, 'dw' stands for double word.
+ * 2, we use '*cursor' and 'buf' as two addresses to calculate the length.
+ * 3, change '0' to '1', if want to get more info about the packet parsing. */
+
+#if 0
+#define PARSER_DEBUG
+#endif
 
 /* read one byte from buf, 
  * return the number of bytes read if succeeds, otherwise return -1 */
-gint read_packet_b(guint8 *buf, guint8 **cursor, gint buflen, guint8 *b)
+gint qq_get8(guint8 *b, guint8 *buf)
 {
-	if (*cursor <= buf + buflen - sizeof(*b)) {
-		*b = **(guint8 **) cursor;
-		*cursor += sizeof(*b);
-		return sizeof(*b);
-	} else {
-		return -1;
-	}
+	guint8 b_dest;
+	memcpy(&b_dest, buf, sizeof(b_dest));
+	*b = b_dest;
+#ifdef PARSER_DEBUG
+	purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][get8] buf %p\n", (void *)buf);
+	purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][get8] b_dest 0x%2x, *b 0x%02x\n", b_dest, *b);
+#endif
+	return sizeof(b_dest);
 }
 
+
 /* read two bytes as "guint16" from buf, 
  * return the number of bytes read if succeeds, otherwise return -1 */
-gint read_packet_w(guint8 *buf, guint8 **cursor, gint buflen, guint16 *w)
+gint qq_get16(guint16 *w, guint8 *buf)
 {
-	if (*cursor <= buf + buflen - sizeof(*w)) {
-		*w = g_ntohs(**(guint16 **) cursor);
-		*cursor += sizeof(*w);
-		return sizeof(*w);
-	} else {
-		return -1;
-	}
+	guint16 w_dest;
+	memcpy(&w_dest, buf, sizeof(w_dest));
+	*w = g_ntohs(w_dest);
+#ifdef PARSER_DEBUG
+	purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][get16] buf %p\n", (void *)buf);
+	purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][get16] w_dest 0x%04x, *w 0x%04x\n", w_dest, *w);
+#endif
+	return sizeof(w_dest);
 }
 
+
 /* read four bytes as "guint32" from buf, 
  * return the number of bytes read if succeeds, otherwise return -1 */
-gint read_packet_dw(guint8 *buf, guint8 **cursor, gint buflen, guint32 *dw)
+gint qq_get32(guint32 *dw, guint8 *buf)
 {
-	if (*cursor <= buf + buflen - sizeof(*dw)) {
-		*dw = g_ntohl(**(guint32 **) cursor);
-		*cursor += sizeof(*dw);
-		return sizeof(*dw);
-	} else {
-		return -1;
-	}
+	guint32 dw_dest;
+	memcpy(&dw_dest, buf, sizeof(dw_dest));
+	*dw = g_ntohl(dw_dest);
+#ifdef PARSER_DEBUG
+	purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][get32] buf %p\n", (void *)buf);
+	purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][get32] dw_dest 0x%08x, *dw 0x%08x\n", dw_dest, *dw);
+#endif
+	return sizeof(dw_dest);
 }
 
+
+/* read datalen bytes from buf, 
+ * return the number of bytes read if succeeds, otherwise return -1 */
+gint qq_getdata(guint8 *data, gint datalen, guint8 *buf)
+{
+    memcpy(data, buf, datalen);
+#ifdef PARSER_DEBUG
+	purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][getdata] buf %p\n", (void *)buf);
+#endif
+    return datalen;
+}
+
+
 /* read four bytes as "time_t" from buf,
  * return the number of bytes read if succeeds, otherwise return -1
  * This function is a wrapper around read_packet_dw() to avoid casting. */
-gint read_packet_time(guint8 *buf, guint8 **cursor, gint buflen, time_t *t)
+gint qq_getime(time_t *t, guint8 *buf)
 {
-	guint32 time;
-	gint ret = read_packet_dw(buf, cursor, buflen, &time);
-	if (ret != -1 ) {
-		*t = time;
-	}
-	return ret;
+	guint32 dw_dest;
+	memcpy(&dw_dest, buf, sizeof(dw_dest));
+#ifdef PARSER_DEBUG
+	purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][getime] buf %p\n", (void *)buf);
+	purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][getime] dw_dest before 0x%08x\n", dw_dest);
+#endif
+	dw_dest = g_ntohl(dw_dest);
+#ifdef PARSER_DEBUG
+	purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][getime] dw_dest after 0x%08x\n", dw_dest);
+#endif
+	memcpy(t, &dw_dest, sizeof(dw_dest));
+	return sizeof(dw_dest);
 }
 
-/* read datalen bytes from buf, 
- * return the number of bytes read if succeeds, otherwise return -1 */
-gint read_packet_data(guint8 *buf, guint8 **cursor, gint buflen, guint8 *data, gint datalen) {
-	if (*cursor <= buf + buflen - datalen) {
-		g_memmove(data, *cursor, datalen);
-		*cursor += datalen;
-		return datalen;
-	} else {
-		return -1;
-	}
+/*------------------------------------------------PUT------------------------------------------------*/
+/* pack one byte into buf
+ * return the number of bytes packed, otherwise return -1 */
+gint qq_put8(guint8 *buf, guint8 b)
+{
+    memcpy(buf, &b, sizeof(b));
+#ifdef PARSER_DEBUG
+	purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][put8] buf %p\n", (void *)buf);
+	purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][put8] b 0x%02x\n", b);
+#endif
+    return sizeof(b);
 }
 
-/* pack one byte into buf
- * return the number of bytes packed, otherwise return -1 */
-gint create_packet_b(guint8 *buf, guint8 **cursor, guint8 b)
-{
-	if (*cursor <= buf + MAX_PACKET_SIZE - sizeof(guint8)) {
-		**(guint8 **) cursor = b;
-		*cursor += sizeof(guint8);
-		return sizeof(guint8);
-	} else {
-		return -1;
-	}
-}
 
 /* pack two bytes as "guint16" into buf
  * return the number of bytes packed, otherwise return -1 */
-gint create_packet_w(guint8 *buf, guint8 **cursor, guint16 w)
+gint qq_put16(guint8 *buf, guint16 w)
 {
-	if (*cursor <= buf + MAX_PACKET_SIZE - sizeof(guint16)) {
-		**(guint16 **) cursor = g_htons(w);
-		*cursor += sizeof(guint16);
-		return sizeof(guint16);
-	} else {
-		return -1;
-	}
+    guint16 w_porter;
+    w_porter = g_htons(w);
+#ifdef PARSER_DEBUG
+	purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][put16] buf %p\n", (void *)buf);
+	purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][put16] w 0x%04x, w_porter 0x%04x\n", w, w_porter);
+#endif
+    memcpy(buf, &w_porter, sizeof(w_porter));
+    return sizeof(w_porter);
 }
 
+
 /* pack four bytes as "guint32" into buf
  * return the number of bytes packed, otherwise return -1 */
-gint create_packet_dw(guint8 *buf, guint8 **cursor, guint32 dw)
+gint qq_put32(guint8 *buf, guint32 dw)
 {
-	if (*cursor <= buf + MAX_PACKET_SIZE - sizeof(guint32)) {
-		**(guint32 **) cursor = g_htonl(dw);
-		*cursor += sizeof(guint32);
-		return sizeof(guint32);
-	} else {
-		return -1;
-	}
+    guint32 dw_porter;
+    dw_porter = g_htonl(dw);
+#ifdef PARSER_DEBUG
+	purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][put32] buf %p\n", (void *)buf);
+	purple_debug(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);
 }
 
+
 /* pack datalen bytes into buf
  * return the number of bytes packed, otherwise return -1 */
-gint create_packet_data(guint8 *buf, guint8 **cursor, guint8 *data, gint datalen)
+gint qq_putdata(guint8 *buf, const guint8 *data, const int datalen)
 {
-	if (*cursor <= buf + MAX_PACKET_SIZE - datalen) {
-		g_memmove(*cursor, data, datalen);
-		*cursor += datalen;
-		return datalen;
-	} else {
-		return -1;
-	}
+    memcpy(buf, data, datalen);
+#ifdef PARSER_DEBUG
+	purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][putdata] buf %p\n", (void *)buf);
+#endif
+    return datalen;
 }
--- a/libpurple/protocols/qq/packet_parse.h	Wed Jul 09 00:27:44 2008 +0000
+++ b/libpurple/protocols/qq/packet_parse.h	Wed Jul 09 00:32:18 2008 +0000
@@ -37,14 +37,28 @@
  */
 #define MAX_PACKET_SIZE 65535
 
+gint qq_get8(guint8 *b, guint8 *buf);
+gint qq_get16(guint16 *w, guint8 *buf);
+gint qq_get32(guint32 *dw,  guint8 *buf);
+gint qq_getime(time_t *t, guint8 *buf);
+gint qq_getdata(guint8 *data, gint datalen, guint8 *buf);
+
+gint qq_put8(guint8 *buf, guint8 b);
+gint qq_put16(guint8 *buf, guint16 w);
+gint qq_put32(guint8 *buf, guint32 dw);
+gint qq_putdata(guint8 *buf, const guint8 *data, const int datalen);
+
+/*
 gint read_packet_b(guint8 *buf, guint8 **cursor, gint buflen, guint8 *b);
 gint read_packet_w(guint8 *buf, guint8 **cursor, gint buflen, guint16 *w);
 gint read_packet_dw(guint8 *buf, guint8 **cursor, gint buflen, guint32 *dw);
 gint read_packet_time(guint8 *buf, guint8 **cursor, gint buflen, time_t *t);
 gint read_packet_data(guint8 *buf, guint8 **cursor, gint buflen, guint8 *data, gint datalen);
+
 gint create_packet_b(guint8 *buf, guint8 **cursor, guint8 b);
 gint create_packet_w(guint8 *buf, guint8 **cursor, guint16 w);
 gint create_packet_dw(guint8 *buf, guint8 **cursor, guint32 dw);
 gint create_packet_data(guint8 *buf, guint8 **cursor, guint8 *data, gint datalen);
+*/
 
 #endif
--- a/libpurple/protocols/qq/qq.c	Wed Jul 09 00:27:44 2008 +0000
+++ b/libpurple/protocols/qq/qq.c	Wed Jul 09 00:32:18 2008 +0000
@@ -55,48 +55,94 @@
 #include "login_logout.h"
 #include "packet_parse.h"
 #include "qq.h"
-#include "qq_proxy.h"
-#include "send_core.h"
+#include "qq_network.h"
 #include "send_file.h"
 #include "utils.h"
 #include "version.h"
 
 #define OPENQ_AUTHOR            "Puzzlebird"
 #define OPENQ_WEBSITE            "http://openq.sourceforge.net"
-#define QQ_TCP_QUERY_PORT       "8000"
-#define QQ_UDP_PORT             "8000"
+
+#define QQ_TCP_PORT       		8000
+#define QQ_UDP_PORT             	8000
+
+static void server_list_create(PurpleAccount *account) {
+	PurpleConnection *gc;
+	qq_data *qd;
+	const gchar *user_server;
+	int port;
+
+	purple_debug(PURPLE_DEBUG_INFO, "QQ", "Create server list\n");
+	gc = purple_account_get_connection(account);
+	g_return_if_fail(gc != NULL  && gc->proto_data != NULL);
+	qd = gc->proto_data;
 
-const gchar *udp_server_list[] = {
-	"sz.tencent.com",
-	"sz2.tencent.com",
-	"sz3.tencent.com",
-	"sz4.tencent.com",
-	"sz5.tencent.com",
-	"sz6.tencent.com",
-	"sz7.tencent.com",
-	"sz8.tencent.com",
-	"sz9.tencent.com"
-};
-const gint udp_server_amount = (sizeof(udp_server_list) / sizeof(udp_server_list[0]));
+	qd->use_tcp = purple_account_get_bool(account, "use_tcp", TRUE);
+	port = purple_account_get_int(account, "port", 0);
+	if (port == 0) {
+		if (qd->use_tcp) {
+			port = QQ_TCP_PORT;
+		} else {
+			port = QQ_UDP_PORT;
+		}
+	}
+	qd->user_port = port;
 
+ 	g_return_if_fail(qd->user_server == NULL);
+	user_server = purple_account_get_string(account, "server", NULL);
+	if (user_server != NULL && strlen(user_server) > 0) {
+		qd->user_server = g_strdup(user_server);
+	}
 
-const gchar *tcp_server_list[] = {
-	"tcpconn.tencent.com",
-	"tcpconn2.tencent.com",
-	"tcpconn3.tencent.com",
-	"tcpconn4.tencent.com",
-	"tcpconn5.tencent.com",
-	"tcpconn6.tencent.com"
-};
-const gint tcp_server_amount = (sizeof(tcp_server_list) / sizeof(tcp_server_list[0]));
+	if (qd->user_server != NULL) {
+		qd->servers = g_list_append(qd->servers, qd->user_server);
+		return;
+	}
+	if (qd->use_tcp) {
+		qd->servers = g_list_append(qd->servers, "tcpconn.tencent.com");
+		qd->servers = g_list_append(qd->servers, "tcpconn2.tencent.com");
+		qd->servers = g_list_append(qd->servers, "tcpconn3.tencent.com");
+		qd->servers = g_list_append(qd->servers, "tcpconn4.tencent.com");
+		qd->servers = g_list_append(qd->servers, "tcpconn5.tencent.com");
+		qd->servers = g_list_append(qd->servers, "tcpconn6.tencent.com");
+		return;
+    }
+    
+	qd->servers = g_list_append(qd->servers, "sz.tencent.com");
+	qd->servers = g_list_append(qd->servers, "sz2.tencent.com");
+	qd->servers = g_list_append(qd->servers, "sz3.tencent.com");
+	qd->servers = g_list_append(qd->servers, "sz4.tencent.com");
+	qd->servers = g_list_append(qd->servers, "sz5.tencent.com");
+	qd->servers = g_list_append(qd->servers, "sz6.tencent.com");
+	qd->servers = g_list_append(qd->servers, "sz7.tencent.com");
+	qd->servers = g_list_append(qd->servers, "sz8.tencent.com");
+	qd->servers = g_list_append(qd->servers, "sz9.tencent.com");
+}
 
-static void _qq_login(PurpleAccount *account)
+static void server_list_remove_all(qq_data *qd) {
+ 	g_return_if_fail(qd != NULL);
+
+	if (qd->real_hostname) {
+		purple_debug(PURPLE_DEBUG_INFO, "QQ", "free real_hostname\n");
+		g_free(qd->real_hostname);
+		qd->real_hostname = NULL;
+	}
+	
+	if (qd->user_server != NULL) {
+		purple_debug(PURPLE_DEBUG_INFO, "QQ", "free user_server\n");
+		g_free(qd->user_server);
+		qd->user_server = NULL;
+	}
+
+	purple_debug(PURPLE_DEBUG_INFO, "QQ", "free server list\n");
+ 	g_list_free(qd->servers);
+}
+
+static void qq_login(PurpleAccount *account)
 {
-	const gchar *qq_server, *qq_port;
+	PurpleConnection *gc;
 	qq_data *qd;
-	PurpleConnection *gc;
 	PurplePresence *presence;
-	gboolean use_tcp;
 
 	g_return_if_fail(account != NULL);
 
@@ -109,13 +155,7 @@
 	qd->gc = gc;
 	gc->proto_data = qd;
 
-	qq_server = purple_account_get_string(account, "server", NULL);
-	qq_port = purple_account_get_string(account, "port", NULL);
-	use_tcp = purple_account_get_bool(account, "use_tcp", FALSE);
 	presence = purple_account_get_presence(account);
-
-	qd->use_tcp = use_tcp;
-
 	if(purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_INVISIBLE)) {
 		qd->login_mode = QQ_LOGIN_MODE_HIDDEN;
 	} else if(purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_AWAY)
@@ -125,26 +165,28 @@
 		qd->login_mode = QQ_LOGIN_MODE_NORMAL;
 	}
 
-	if (qq_server == NULL || strlen(qq_server) == 0)
-		qq_server = use_tcp ?
-		    tcp_server_list[random() % tcp_server_amount] :
-		    udp_server_list[random() % udp_server_amount];
+	server_list_create(account);
+	purple_debug(PURPLE_DEBUG_INFO, "QQ",
+		"Server list has %d\n", g_list_length(qd->servers));
 
-	if (qq_port == NULL || strtol(qq_port, NULL, 10) == 0)
-		qq_port = use_tcp ? QQ_TCP_QUERY_PORT : QQ_UDP_PORT;
-
-	purple_connection_update_progress(gc, _("Connecting"), 0, QQ_CONNECT_STEPS);
-
-	if (qq_connect(account, qq_server, strtol(qq_port, NULL, 10), use_tcp, FALSE) < 0)
-		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
-			_("Unable to connect."));
+	qq_connect(account);
 }
 
-/* directly goes for qq_disconnect */
-static void _qq_close(PurpleConnection *gc)
+/* clean up the given QQ connection and free all resources */
+static void qq_close(PurpleConnection *gc)
 {
-	g_return_if_fail(gc != NULL);
+	qq_data *qd;
+
+	g_return_if_fail(gc != NULL  && gc->proto_data);
+	qd = gc->proto_data;
+
 	qq_disconnect(gc);
+
+	server_list_remove_all(qd);
+	
+	g_free(qd);
+
+	gc->proto_data = NULL;
 }
 
 /* returns the icon name for a buddy or protocol */
@@ -442,8 +484,9 @@
 
 	g_string_append(info, "<hr>\n");
 
+	g_string_append_printf(info, _("<b>Server</b>: %s: %d<br>\n"), qd->server_name, qd->real_port);
 	g_string_append_printf(info, _("<b>Connection Mode</b>: %s<br>\n"), qd->use_tcp ? "TCP" : "UDP");
-	g_string_append_printf(info, _("<b>Server IP</b>: %s: %d<br>\n"), qd->server_ip, qd->server_port);
+	g_string_append_printf(info, _("<b>Real hostname</b>: %s: %d<br>\n"), qd->real_hostname, qd->real_port);
 	g_string_append_printf(info, _("<b>My Public IP</b>: %s<br>\n"), qd->my_ip);
 
 	g_string_append(info, "<hr>\n");
@@ -594,7 +637,7 @@
 }
 
 
-static void _qq_keep_alive(PurpleConnection *gc)
+static void qq_keep_alive(PurpleConnection *gc)
 {
 	qq_group *group;
 	qq_data *qd;
@@ -651,8 +694,8 @@
 	_qq_buddy_menu,						/* blist_node_menu */
 	qq_chat_info,						/* chat_info */
 	qq_chat_info_defaults,					/* chat_info_defaults */
-	_qq_login,						/* login */
-	_qq_close,						/* close */
+	qq_login,							/* open */
+	qq_close,						/* close */
 	_qq_send_im,						/* send_im */
 	NULL,							/* set_info */
 	NULL,							/* send_typing	*/
@@ -676,7 +719,7 @@
 	NULL,							/* chat_leave */
 	NULL,							/* chat_whisper */
 	_qq_chat_send,						/* chat_send */
-	_qq_keep_alive,						/* keepalive */
+	qq_keep_alive,						/* keepalive */
 	NULL,							/* register_user */
 	_qq_get_chat_buddy_info,				/* get_cb_info	*/
 	NULL,							/* get_cb_away	*/
@@ -751,13 +794,13 @@
 {
 	PurpleAccountOption *option;
 
-	option = purple_account_option_bool_new(_("Connect using TCP"), "use_tcp", FALSE);
-	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
-
 	option = purple_account_option_string_new(_("Server"), "server", NULL);
 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
 
-	option = purple_account_option_string_new(_("Port"), "port", NULL);
+	option = purple_account_option_int_new(_("Port"), "port", 0);
+	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
+
+	option = purple_account_option_bool_new(_("Connect using TCP"), "use_tcp", TRUE);
 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
 
 	my_protocol = plugin;
--- a/libpurple/protocols/qq/qq.h	Wed Jul 09 00:27:44 2008 +0000
+++ b/libpurple/protocols/qq/qq.h	Wed Jul 09 00:32:18 2008 +0000
@@ -28,6 +28,9 @@
 #include <glib.h>
 #include "internal.h"
 #include "ft.h"
+#include "circbuffer.h"
+#include "dnsquery.h"
+#include "dnssrv.h"
 #include "proxy.h"
 #include "roomlist.h"
 
@@ -66,7 +69,39 @@
 };
 
 struct _qq_data {
-	gint fd;			/* socket file handler */
+	PurpleConnection *gc;
+
+	/* common network resource */
+	GList *servers;
+	gchar *user_server;
+	gint user_port;
+	gboolean use_tcp;		/* network in tcp or udp */
+	
+	gchar *server_name;
+	gboolean is_redirect;
+	gchar *real_hostname;	/* from real connction */
+	guint16 real_port;
+	guint reconnect_timeout;
+	gint reconnect_times;
+
+	PurpleProxyConnectData *connect_data;
+	gint fd;				/* socket file handler */
+	gint tx_handler; 	/* socket can_write handle, use in udp connecting and tcp send out */
+
+	GList *send_trans;	/* check ack packet and resend */
+	guint resend_timeout;
+
+	guint8 rcv_window[1 << 13];		/* windows for check duplicate packet */
+	GQueue *rcv_trans;		/* queue to store packet can not process before login */
+	
+	/* tcp related */
+	PurpleCircBuffer *tcp_txbuf;
+	guint8 *tcp_rxqueue;
+	int tcp_rxlen;
+	
+	/* udp related */
+	PurpleDnsQueryData *udp_query_data;
+
 	guint32 uid;			/* QQ number */
 	guint8 *inikey;			/* initial key to encrypt login packet */
 	guint8 *pwkey;			/* password in md5 (or md5' md5) */
@@ -76,17 +111,9 @@
 	guint16 send_seq;		/* send sequence number */
 	guint8 login_mode;		/* online of invisible */
 	gboolean logged_in;		/* used by qq-add_buddy */
-	gboolean use_tcp;		/* network in tcp or udp */
-
-	PurpleProxyType proxy_type;
-	PurpleConnection *gc;
 
 	PurpleXfer *xfer;			/* file transfer handler */
-	struct sockaddr_in dest_sin;
 
-	/* from real connction */
-	gchar *server_ip;
-	guint16 server_port;
 	/* get from login reply packet */
 	time_t login_time;
 	time_t last_login_time;
@@ -99,9 +126,6 @@
 	guint32 all_online;		/* the number of online QQ users */
 	time_t last_get_online;		/* last time send get_friends_online packet */
 
-	guint8 window[1 << 13];		/* check up for duplicated packet */
-	gint sendqueue_timeout;
-
 	PurpleRoomlist *roomlist;
 	gint channel;			/* the id for opened chat conversation */
 
@@ -112,16 +136,12 @@
 	GList *buddies;
 	GList *contact_info_window;
 	GList *group_info_window;
-	GList *sendqueue;
 	GList *info_query;
 	GList *add_buddy_request;
-	GQueue *before_login_packets;
 
 	/* TODO pass qq_send_packet_get_info() a callback and use signals to get rid of these */
 	gboolean modifying_info;
 	gboolean modifying_face;
 };
 
-void qq_function_not_implemented(PurpleConnection *gc);
-
 #endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/qq/qq_network.c	Wed Jul 09 00:32:18 2008 +0000
@@ -0,0 +1,1258 @@
+/**
+ * @file qq_network.c
+ *
+ * purple
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
+ */
+
+#include "cipher.h"
+#include "debug.h"
+#include "internal.h"
+
+#ifdef _WIN32
+#define random rand
+#define srandom srand
+#endif
+
+#include "buddy_info.h"
+#include "buddy_list.h"
+#include "buddy_opt.h"
+#include "buddy_status.h"
+#include "group_free.h"
+#include "char_conv.h"
+#include "crypt.h"
+#include "group_network.h"
+#include "header_info.h"
+#include "keep_alive.h"
+#include "im.h"
+#include "login_logout.h"
+#include "packet_parse.h"
+#include "qq_network.h"
+#include "qq_trans.h"
+#include "sys_msg.h"
+#include "utils.h"
+
+/* set QQ_RECONNECT_MAX to 1, when test reconnecting */
+#define QQ_RECONNECT_MAX					4
+#define QQ_RECONNECT_INTERVAL		5000
+
+static gboolean set_new_server(qq_data *qd)
+{
+	gint count;
+	gint index;
+	GList *it = NULL;
+	
+ 	g_return_val_if_fail(qd != NULL, FALSE);
+
+	if (qd->servers == NULL) {
+		purple_debug(PURPLE_DEBUG_INFO, "QQ", "Server list is NULL\n");
+		return FALSE;
+	}
+
+	if (qd->real_hostname) {
+		purple_debug(PURPLE_DEBUG_INFO, "QQ", "free real_hostname\n");
+		g_free(qd->real_hostname);
+		qd->real_hostname = NULL;
+	}
+
+	/* remove server used before */
+	if (qd->server_name != NULL) {
+		purple_debug(PURPLE_DEBUG_INFO, "QQ",
+			"Remove previous server [%s]\n", qd->server_name);
+   		qd->servers = g_list_remove(qd->servers, qd->server_name);
+   		qd->server_name = NULL;
+    }
+	
+	count = g_list_length(qd->servers);
+	purple_debug(PURPLE_DEBUG_INFO, "QQ", "Server list has %d\n", count);
+	if (count <= 0) {
+		/* no server left, disconnect when result is false */
+		qd->servers = NULL;
+		return FALSE;
+	}
+	
+	/* get new server */
+	index  = random() % count;
+	it = g_list_nth(qd->servers, index);
+    qd->server_name = it->data;		/* do not free server_name */
+    if (qd->server_name == NULL || strlen(qd->server_name) <= 0 ) {
+		purple_debug(PURPLE_DEBUG_INFO, "QQ", "Server name at %d is empty\n", index);
+		return FALSE;
+	}
+
+	qd->real_hostname = g_strdup(qd->server_name);
+	qd->real_port = qd->user_port;
+	
+ 	qd->reconnect_times = QQ_RECONNECT_MAX;
+
+	purple_debug(PURPLE_DEBUG_INFO, "QQ",
+		"set new server to %s:%d\n", qd->real_hostname, qd->real_port);
+	return TRUE;
+}
+
+/* QQ 2003iii uses double MD5 for the pwkey to get the session key */
+static guint8 *encrypt_account_password(const gchar *pwd)
+{
+	PurpleCipher *cipher;
+	PurpleCipherContext *context;
+
+	guchar pwkey_tmp[QQ_KEY_LENGTH];
+
+	cipher = purple_ciphers_find_cipher("md5");
+	context = purple_cipher_context_new(cipher, NULL);
+	purple_cipher_context_append(context, (guchar *) pwd, strlen(pwd));
+	purple_cipher_context_digest(context, sizeof(pwkey_tmp), pwkey_tmp, NULL);
+	purple_cipher_context_destroy(context);
+	context = purple_cipher_context_new(cipher, NULL);
+	purple_cipher_context_append(context, pwkey_tmp, QQ_KEY_LENGTH);
+	purple_cipher_context_digest(context, sizeof(pwkey_tmp), pwkey_tmp, NULL);
+	purple_cipher_context_destroy(context);
+
+	return g_memdup(pwkey_tmp, QQ_KEY_LENGTH);
+}
+
+/* default process, decrypt and dump */
+static void process_cmd_unknow(PurpleConnection *gc, guint8 *buf, gint buf_len, guint16 cmd, guint16 seq)
+{
+	qq_data *qd;
+	guint8 *data;
+	gint data_len;
+	gchar *msg_utf8 = NULL;
+
+	g_return_if_fail(buf != NULL && buf_len != 0);
+
+	qq_show_packet("Processing unknown packet", buf, buf_len);
+
+	qd = (qq_data *) gc->proto_data;
+
+	data_len = buf_len;
+	data = g_newa(guint8, data_len);
+	memset(data, 0, data_len);
+	if ( !qq_decrypt(buf, buf_len, qd->session_key, data, &data_len )) {
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Fail decrypt packet with default process\n");
+		return;
+	}
+	
+	qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ",
+			data, data_len,
+			">>> [%d] %s -> [default] decrypt and dump",
+			seq, qq_get_cmd_desc(cmd));
+
+	msg_utf8 = try_dump_as_gbk(data, data_len);
+	if (msg_utf8) {
+		g_free(msg_utf8);
+	}
+}
+
+static gint packet_get_header(guint8 *header_tag,  guint16 *source_tag,
+	guint16 *cmd, guint16 *seq, guint8 *buf)
+{
+	gint bytes = 0;
+	bytes += qq_get8(header_tag, buf + bytes);
+	bytes += qq_get16(source_tag, buf + bytes);
+	bytes += qq_get16(cmd, buf + bytes);
+	bytes += qq_get16(seq, buf + bytes);
+	return bytes;
+}
+
+/* check whether one sequence number is duplicated or not
+ * return TRUE if it is duplicated, otherwise FALSE */
+static gboolean packet_is_dup(qq_data *qd, guint16 seq)
+{
+	guint8 *byte, mask;
+
+	g_return_val_if_fail(qd != NULL, FALSE);
+
+	byte = &(qd->rcv_window[seq / 8]);
+	mask = (1 << (seq % 8));
+
+	if ((*byte) & mask)
+		return TRUE;	/* check mask */
+	(*byte) |= mask;
+	return FALSE;		/* set mask */
+}
+
+static gboolean packet_check_ack(qq_data *qd, guint16 seq)
+{
+	gpointer trans;
+
+	g_return_val_if_fail(qd != NULL, FALSE);
+
+	trans = qq_send_trans_find(qd, seq);
+	if (trans == NULL) {
+		return FALSE;
+	}
+	
+	qq_send_trans_remove(qd, trans);
+	return TRUE;
+}
+
+static gboolean reconnect_later_cb(gpointer data)
+{
+	PurpleConnection *gc;
+	qq_data *qd;
+
+	gc = (PurpleConnection *) data;
+	g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, FALSE);
+	qd = (qq_data *) gc->proto_data;
+
+	qd->reconnect_timeout = 0;
+
+	qq_connect(gc->account);
+	return FALSE;	/* timeout callback stops */
+}
+
+static void reconnect_later(PurpleConnection *gc)
+{
+	qq_data *qd;
+
+	g_return_if_fail(gc != NULL && gc->proto_data != NULL);
+	qd = (qq_data *) gc->proto_data;
+
+	qd->reconnect_times--;
+	if (qd->reconnect_times < 0) {
+		if ( set_new_server(qd) != TRUE) {
+			purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+					_("Failed to connect server"));
+			return;
+		}
+	}
+
+	purple_debug(PURPLE_DEBUG_INFO, "QQ",
+		"Reconnect to server %s:%d next retries %d in %d ms\n",
+		qd->real_hostname, qd->real_port,
+		qd->reconnect_times, QQ_RECONNECT_INTERVAL);
+
+	qd->reconnect_timeout = purple_timeout_add(QQ_RECONNECT_INTERVAL,
+		reconnect_later_cb, gc);
+}
+
+static void process_cmd_server(
+	PurpleConnection *gc, guint16 cmd, guint16 seq, guint8 *data, gint data_len)
+{
+	/* now process the packet */
+	switch (cmd) {
+		case QQ_CMD_RECV_IM:
+			qq_process_recv_im(data, data_len, seq, gc);
+			break;
+		case QQ_CMD_RECV_MSG_SYS:
+			qq_process_msg_sys(data, data_len, seq, gc);
+			break;
+		case QQ_CMD_RECV_MSG_FRIEND_CHANGE_STATUS:
+			qq_process_friend_change_status(data, data_len, gc);
+			break;
+		default:
+			process_cmd_unknow(gc, data, data_len, cmd, seq);
+			break;
+	}
+}
+
+static void process_cmd_reply(
+	PurpleConnection *gc, guint16 cmd, guint16 seq, guint8 *data, gint data_len)
+{
+	/* now process the packet */
+	switch (cmd) {
+		case QQ_CMD_KEEP_ALIVE:
+			qq_process_keep_alive_reply(data, data_len, gc);
+			break;
+		case QQ_CMD_UPDATE_INFO:
+			qq_process_modify_info_reply(data, data_len, gc);
+			break;
+		case QQ_CMD_ADD_FRIEND_WO_AUTH:
+			qq_process_add_buddy_reply(data, data_len, seq, gc);
+			break;
+		case QQ_CMD_DEL_FRIEND:
+			qq_process_remove_buddy_reply(data, data_len, gc);
+			break;
+		case QQ_CMD_REMOVE_SELF:
+			qq_process_remove_self_reply(data, data_len, gc);
+			break;
+		case QQ_CMD_BUDDY_AUTH:
+			qq_process_add_buddy_auth_reply(data, data_len, gc);
+			break;
+		case QQ_CMD_GET_USER_INFO:
+			qq_process_get_info_reply(data, data_len, gc);
+			break;
+		case QQ_CMD_CHANGE_ONLINE_STATUS:
+			qq_process_change_status_reply(data, data_len, gc);
+			break;
+		case QQ_CMD_SEND_IM:
+			qq_process_send_im_reply(data, data_len, gc);
+			break;
+		case QQ_CMD_LOGIN:
+			qq_process_login_reply(data, data_len, gc);
+			break;
+		case QQ_CMD_GET_FRIENDS_LIST:
+			qq_process_get_buddies_list_reply(data, data_len, gc);
+			break;
+		case QQ_CMD_GET_FRIENDS_ONLINE:
+			qq_process_get_buddies_online_reply(data, data_len, gc);
+			break;
+		case QQ_CMD_GROUP_CMD:
+			qq_process_group_cmd_reply(data, data_len, seq, gc);
+			break;
+		case QQ_CMD_GET_ALL_LIST_WITH_GROUP:
+			qq_process_get_all_list_with_group_reply(data, data_len, gc);
+			break;
+		case QQ_CMD_GET_LEVEL:
+			qq_process_get_level_reply(data, data_len, gc);
+			break;
+		case QQ_CMD_REQUEST_LOGIN_TOKEN:
+			qq_process_request_login_token_reply(data, data_len, gc);
+			break;
+		default:
+			process_cmd_unknow(gc, data, data_len, cmd, seq);
+			break;
+	}
+}
+
+/* process the incoming packet from qq_pending */
+static void packet_process(PurpleConnection *gc, guint8 *buf, gint buf_len)
+{
+	qq_data *qd;
+	gint bytes, bytes_not_read;
+
+	gboolean prev_login_status;
+	guint8 *new_data;
+	gint new_data_len;
+	
+	guint8 header_tag;
+	guint16 source_tag;
+	guint16 cmd;
+	guint16 seq;		/* May be ack_seq or send_seq, depends on cmd */
+
+	gboolean is_reply;
+
+	g_return_if_fail(buf != NULL && buf_len > 0);
+
+	qd = (qq_data *) gc->proto_data;
+
+	prev_login_status = qd->logged_in;
+
+	/* Len, header and tail tag have been checked before */
+	bytes = 0;
+	bytes += packet_get_header(&header_tag, &source_tag, &cmd, &seq, buf + bytes);
+
+	if (QQ_DEBUG) {
+		purple_debug(PURPLE_DEBUG_INFO, "QQ",
+				"==> [%05d] 0x%04X %s, from (0x%04X %s)\n",
+				seq, cmd, qq_get_cmd_desc(cmd), source_tag, qq_get_source_str(source_tag));
+	}
+	
+	bytes_not_read = buf_len - bytes - 1;
+
+	/* ack packet, we need to update send tranactions */
+	/* we do not check duplication for server ack */
+	is_reply = packet_check_ack(qd, seq);
+	if ( !is_reply ) {
+		if ( !qd->logged_in ) {
+			/* packets before login */
+			qq_rcv_trans_push(qd, cmd, seq, buf + bytes, bytes_not_read);
+			return;	/* do not process it now */
+		}
+		
+		/* server intiated packet, we need to send ack and check duplicaion 
+		 * this must be put after processing b4_packet
+		 * as these packets will be passed in twice */
+		if (packet_is_dup(qd, seq)) {
+			purple_debug(PURPLE_DEBUG_WARNING,
+					"QQ", "dup [%05d] %s, discard...\n", seq, qq_get_cmd_desc(cmd));
+			return;
+		}
+		process_cmd_server(gc, cmd, seq, buf + bytes, bytes_not_read);
+		return;
+	}
+
+	/* this is the length of all the encrypted data (also remove tail tag */
+	process_cmd_reply(gc, cmd, seq, buf + bytes, bytes_not_read);
+
+	/* check is redirect or not, and do it now */
+	if (qd->is_redirect) {
+	 	/* free resource except real_hostname and port */
+		qq_disconnect(gc);
+	 	qd->reconnect_times = QQ_RECONNECT_MAX;
+		reconnect_later(gc);
+		return;
+	}
+
+	if (prev_login_status != qd->logged_in && qd->logged_in == TRUE) {
+		/* logged_in, but we have packets before login */
+		new_data = g_newa(guint8, MAX_PACKET_SIZE);
+		while (1) {
+			memset(new_data, 0, MAX_PACKET_SIZE);
+			new_data_len = qq_rcv_trans_pop(qd, &cmd, &seq, new_data, MAX_PACKET_SIZE);
+			if (new_data_len < 0) {
+				break;
+			}
+			if (new_data_len == 0) {
+				continue;
+			}
+			process_cmd_reply(gc, seq, cmd, new_data, new_data_len);
+		}
+	}
+}
+
+static void tcp_pending(gpointer data, gint source, PurpleInputCondition cond)
+{
+	PurpleConnection *gc;
+	qq_data *qd;
+	guint8 buf[1024];		/* set to 16 when test  tcp_rxqueue */
+	gint buf_len;
+	gint bytes;
+	
+	guint8 *pkt;
+	guint16 pkt_len;
+	
+	gchar *error_msg;
+	guint8 *jump;
+	gint jump_len;
+
+	gc = (PurpleConnection *) data;
+	g_return_if_fail(gc != NULL && gc->proto_data != NULL);
+
+	if(cond != PURPLE_INPUT_READ) {
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+				_("Socket error"));
+		return;
+	}
+
+	qd = (qq_data *) gc->proto_data;
+	
+	/* test code, not using tcp_rxqueue
+	memset(pkt,0, sizeof(pkt));
+	buf_len = read(qd->fd, pkt, sizeof(pkt));
+	if (buf_len > 2) {
+		packet_process(gc, pkt + 2, buf_len - 2);
+	}
+	return;
+	*/
+	
+	buf_len = read(qd->fd, buf, sizeof(buf));
+	if (buf_len < 0) {
+		if (errno == EAGAIN)
+			/* No worries */
+			return;
+
+		error_msg = g_strdup_printf(_("Lost connection with server:\n%d, %s"), errno, g_strerror(errno));
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_msg);
+		g_free(error_msg);
+		return;
+	} else if (buf_len == 0) {
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+				_("Server closed the connection."));
+		return;
+	}
+
+	gc->last_received = time(NULL);
+	purple_debug(PURPLE_DEBUG_INFO, "TCP_PENDING",
+			   "Read %d bytes from socket, rxlen is %d\n", buf_len, qd->tcp_rxlen);
+	qd->tcp_rxqueue = g_realloc(qd->tcp_rxqueue, buf_len + qd->tcp_rxlen);
+	memcpy(qd->tcp_rxqueue + qd->tcp_rxlen, buf, buf_len);
+	qd->tcp_rxlen += buf_len;
+	
+	pkt = g_newa(guint8, MAX_PACKET_SIZE);
+	while (1) {
+		if (qd->tcp_rxlen < QQ_TCP_HEADER_LENGTH) {
+			break;
+		}
+		
+		bytes = 0;
+		bytes += qq_get16(&pkt_len, qd->tcp_rxqueue + bytes);
+		if (qd->tcp_rxlen < pkt_len) {
+			break;
+		}
+
+		purple_debug(PURPLE_DEBUG_INFO, "TCP_PENDING",
+				   "Packet len is %d bytes, rxlen is %d\n", pkt_len, qd->tcp_rxlen);
+
+		if ( pkt_len < QQ_TCP_HEADER_LENGTH
+		    || *(qd->tcp_rxqueue + bytes) != QQ_PACKET_TAG
+			|| *(qd->tcp_rxqueue + pkt_len - 1) != QQ_PACKET_TAIL) {
+			/* HEY! This isn't even a QQ. What are you trying to pull? */
+
+			purple_debug(PURPLE_DEBUG_ERROR, "TCP_PENDING",
+				 "Packet error, failed to check header and tail tag\n");
+
+			jump = memchr(qd->tcp_rxqueue + 1, QQ_PACKET_TAIL, qd->tcp_rxlen - 1);
+			if ( !jump ) {
+				purple_debug(PURPLE_DEBUG_INFO, "TCP_PENDING",
+				 	"Failed to find next QQ_PACKET_TAIL, clear receive buffer\n");
+				g_free(qd->tcp_rxqueue);
+				qd->tcp_rxqueue = NULL;
+				qd->tcp_rxlen = 0;
+				return;
+			}
+
+			/* jump and over QQ_PACKET_TAIL */
+			jump_len = (jump - qd->tcp_rxqueue) + 1;
+			purple_debug(PURPLE_DEBUG_INFO, "TCP_PENDING",
+				"Find next QQ_PACKET_TAIL at %d, jump %d bytes\n", jump_len, jump_len + 1);
+			g_memmove(qd->tcp_rxqueue, jump, qd->tcp_rxlen - jump_len);
+			qd->tcp_rxlen -= jump_len;
+			continue;
+		}
+
+		memset(pkt, 0, MAX_PACKET_SIZE);
+		g_memmove(pkt, qd->tcp_rxqueue + bytes, pkt_len - bytes);
+		
+		/* jump to next packet */
+		qd->tcp_rxlen -= pkt_len;
+		if (qd->tcp_rxlen) {
+			purple_debug(PURPLE_DEBUG_ERROR, "TCP_PENDING",
+			 	"shrink tcp_rxqueue to %d\n", qd->tcp_rxlen);		
+			jump = g_memdup(qd->tcp_rxqueue + pkt_len, qd->tcp_rxlen);
+			g_free(qd->tcp_rxqueue);
+			qd->tcp_rxqueue = jump;
+		} else {
+			purple_debug(PURPLE_DEBUG_ERROR, "TCP_PENDING",
+			 	"free tcp_rxqueue\n");		
+			g_free(qd->tcp_rxqueue);
+			qd->tcp_rxqueue = NULL;
+		}
+
+		if (pkt == NULL) {
+			continue;
+		}
+		/* do not call packet_process before jump 
+		 * packet_process may call disconnect and destory tcp_rxqueue */
+		packet_process(gc, pkt, pkt_len - bytes);
+	}
+}
+
+static void udp_pending(gpointer data, gint source, PurpleInputCondition cond)
+{
+	PurpleConnection *gc;
+	qq_data *qd;
+	guint8 *buf;
+	gint buf_len;
+
+	gc = (PurpleConnection *) data;
+	g_return_if_fail(gc != NULL && gc->proto_data != NULL);
+
+	if(cond != PURPLE_INPUT_READ) {
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+				_("Socket error"));
+		return;
+	}
+
+	qd = (qq_data *) gc->proto_data;
+	g_return_if_fail(qd->fd >= 0);
+	
+	buf = g_newa(guint8, MAX_PACKET_SIZE);
+
+	/* here we have UDP proxy suppport */
+	buf_len = read(qd->fd, buf, MAX_PACKET_SIZE);
+	if (buf_len <= 0) {
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+				_("Unable to read from socket"));
+		return;
+	}
+
+	gc->last_received = time(NULL);
+
+	if (buf_len < QQ_UDP_HEADER_LENGTH) {
+		if (buf[0] != QQ_PACKET_TAG || buf[buf_len - 1] != QQ_PACKET_TAIL) {
+			qq_hex_dump(PURPLE_DEBUG_ERROR, "UDP_PENDING",
+					buf, buf_len,
+					"Received packet is too short, or no header and tail tag");
+			return;
+		}
+	}
+	
+	packet_process(gc, buf, buf_len);
+}
+
+static gint udp_send_out(qq_data *qd, guint8 *data, gint data_len)
+{
+	gint ret;
+
+	g_return_val_if_fail(qd != NULL && qd->fd >= 0 && data != NULL && data_len > 0, -1);
+
+	purple_debug(PURPLE_DEBUG_INFO, "QQ", "Send %d bytes to socket %d\n", data_len, qd->fd);
+
+	errno = 0;
+	ret = send(qd->fd, data, data_len, 0);
+	if (ret < 0 && errno == EAGAIN) {
+		return ret;
+	}
+	
+	if (ret < 0) {
+		/* TODO: what to do here - do we really have to disconnect? */
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Send failed: %d, %s\n", errno, g_strerror(errno));
+		purple_connection_error_reason(qd->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, g_strerror(errno));
+	}
+	return ret;
+}
+
+static void tcp_can_write(gpointer data, gint source, PurpleInputCondition cond)
+{
+	qq_data *qd = data;
+	int ret, writelen;
+
+	writelen = purple_circ_buffer_get_max_read(qd->tcp_txbuf);
+	if (writelen == 0) {
+		purple_input_remove(qd->tx_handler);
+		qd->tx_handler = 0;
+		return;
+	}
+
+	ret = write(qd->fd, qd->tcp_txbuf->outptr, writelen);
+	purple_debug(PURPLE_DEBUG_ERROR, "TCP_CAN_WRITE",
+		"total %d bytes is sent %d\n", writelen, ret);
+
+	if (ret < 0 && errno == EAGAIN)
+		return;
+	else if (ret < 0) {
+		/* TODO: what to do here - do we really have to disconnect? */
+		purple_connection_error_reason(qd->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+		                               _("Write Error"));
+		return;
+	}
+
+	purple_circ_buffer_mark_read(qd->tcp_txbuf, ret);
+}
+
+static gint tcp_send_out(qq_data *qd, guint8 *data, gint data_len)
+{
+	gint ret;
+
+	g_return_val_if_fail(qd != NULL && qd->fd >= 0 && data != NULL && data_len > 0, -1);
+
+	/*
+	 * purple_debug(PURPLE_DEBUG_INFO, "TCP_SEND_OUT", "Send %d bytes to socket %d\n", data_len, qd->fd);
+	 */
+
+	if (qd->tx_handler == 0) {
+		ret = write(qd->fd, data, data_len);
+	} else {
+		ret = -1;
+		errno = EAGAIN;
+	}
+
+	purple_debug(PURPLE_DEBUG_INFO, "TCP_SEND_OUT",
+		"Socket %d, total %d bytes is sent %d\n", qd->fd, data_len, ret);
+	if (ret < 0 && errno == EAGAIN) {
+		/* socket is busy, send later */
+		/*
+		 * purple_debug(PURPLE_DEBUG_INFO, "TCP_SEND_OUT", "Socket is busy and send later\n");
+		 */
+		ret = 0;
+	} else if (ret <= 0) {
+		/* TODO: what to do here - do we really have to disconnect? */
+		purple_debug(PURPLE_DEBUG_ERROR, "TCP_SEND_OUT",
+			"Send to socket %d failed: %d, %s\n", qd->fd, errno, g_strerror(errno));
+		purple_connection_error_reason(qd->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, g_strerror(errno));
+		return ret;
+	}
+
+	if (ret < data_len) {
+		purple_debug(PURPLE_DEBUG_INFO, "TCP_SEND_OUT",
+			"Add %d bytes to buffer\n", data_len - ret);
+		if (qd->tx_handler == 0) {
+			qd->tx_handler = purple_input_add(qd->fd, PURPLE_INPUT_WRITE, tcp_can_write, qd);
+		}
+		purple_circ_buffer_append(qd->tcp_txbuf, data + ret, data_len - ret);
+	}
+	return ret;
+}
+
+static gboolean trans_timeout(gpointer data)
+{
+	PurpleConnection *gc;
+	qq_data *qd;
+	guint8 *buf;
+	gint buf_len = 0;
+	guint16 cmd;
+	gint retries = 0;
+	int index;
+	
+	gc = (PurpleConnection *) data;
+	g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, TRUE);
+
+	qd = (qq_data *) gc->proto_data;
+	
+	index = 0;
+	buf = g_newa(guint8, MAX_PACKET_SIZE);
+
+	while (1) {
+		if (index < 0) {
+			/* next record is NULL */
+			break;
+		}
+		/* purple_debug(PURPLE_DEBUG_ERROR, "QQ", "scan begin %d\n", index); */
+		memset(buf, 0, MAX_PACKET_SIZE);
+		buf_len = qq_send_trans_scan(qd, &index, buf, MAX_PACKET_SIZE, &cmd, &retries);
+		if (buf_len <= 0) {
+			/* curr record is empty, whole trans  is NULL */
+			break;
+		}
+		/* index = -1, when get last record of transactions */
+		
+		/* purple_debug(PURPLE_DEBUG_ERROR, "QQ", "retries %d next index %d\n", retries, index); */
+		if (retries > 0) {
+			if (qd->use_tcp) {
+				tcp_send_out(qd, buf, buf_len);
+			} else {
+				udp_send_out(qd, buf, buf_len);
+			}
+			continue;
+		}
+
+		/* retries <= 0 */
+		switch (cmd) {
+		case QQ_CMD_KEEP_ALIVE:
+			if (qd->logged_in) {
+				purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Connection lost!\n");
+				purple_connection_error_reason(gc,
+					PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Connection lost"));
+				qd->logged_in = FALSE;
+			}
+			break;
+		case QQ_CMD_LOGIN:
+		case QQ_CMD_REQUEST_LOGIN_TOKEN:
+			if (!qd->logged_in)	{
+				/* cancel login progress */
+				purple_connection_error_reason(gc,
+					PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Login failed, no reply"));
+			}
+			break;
+		default:
+			purple_debug(PURPLE_DEBUG_WARNING, "QQ", 
+				"%s packet lost.\n", qq_get_cmd_desc(cmd));
+		}
+	}
+
+	return TRUE;		/* if return FALSE, timeout callback stops */
+}
+
+/* the callback function after socket is built
+ * we setup the qq protocol related configuration here */
+static void qq_connect_cb(gpointer data, gint source, const gchar *error_message)
+{
+	qq_data *qd;
+	PurpleConnection *gc;
+	gchar *conn_msg;
+	const gchar *passwd;
+
+	gc = (PurpleConnection *) data;
+
+	if (!PURPLE_CONNECTION_IS_VALID(gc)) {
+		purple_debug(PURPLE_DEBUG_INFO, "QQ_CONN", "Invalid connection\n");
+		close(source);
+		return;
+	}
+
+	g_return_if_fail(gc != NULL && gc->proto_data != NULL);
+
+	qd = (qq_data *) gc->proto_data;
+
+	/* Connect is now complete; clear the PurpleProxyConnectData */
+	qd->connect_data = NULL;
+
+	if (source < 0) {	/* socket returns -1 */
+		purple_debug(PURPLE_DEBUG_INFO, "QQ_CONN", "Invalid connection, source is < 0\n");
+		qq_disconnect(gc);
+		reconnect_later(gc);
+		return;
+	}
+
+	/* _qq_show_socket("Got login socket", source); */
+
+	/* QQ use random seq, to minimize duplicated packets */
+	srandom(time(NULL));
+	qd->send_seq = random() & 0x0000ffff;
+	qd->fd = source;
+	qd->logged_in = FALSE;
+	qd->channel = 1;
+	qd->uid = strtol(purple_account_get_username(purple_connection_get_account(gc)), NULL, 10);
+
+	/* now generate md5 processed passwd */
+	passwd = purple_account_get_password(purple_connection_get_account(gc));
+	g_return_if_fail(qd->pwkey == NULL);
+	qd->pwkey = encrypt_account_password(passwd);
+
+	g_return_if_fail(qd->resend_timeout == 0);
+	/* call trans_timeout every 5 seconds */
+	qd->resend_timeout = purple_timeout_add(5000, trans_timeout, gc);
+	
+	if (qd->use_tcp)
+		gc->inpa = purple_input_add(qd->fd, PURPLE_INPUT_READ, tcp_pending, gc);
+	else
+		gc->inpa = purple_input_add(qd->fd, PURPLE_INPUT_READ, udp_pending, gc);
+
+	/* Update the login progress status display */
+	conn_msg = g_strdup_printf("Login as %d", qd->uid);
+	purple_connection_update_progress(gc, conn_msg, QQ_CONNECT_STEPS - 1, QQ_CONNECT_STEPS);
+	g_free(conn_msg);
+
+	qq_send_packet_request_login_token(gc);
+}
+
+static void udp_can_write(gpointer data, gint source, PurpleInputCondition cond)
+{
+	PurpleConnection *gc;
+	qq_data *qd;
+	socklen_t len;
+	int error=0, ret;
+
+	gc = (PurpleConnection *) data;
+	g_return_if_fail(gc != NULL && gc->proto_data != NULL);
+
+	qd = (qq_data *) gc->proto_data;
+
+
+	purple_debug_info("proxy", "Connected.\n");
+
+	/*
+	 * getsockopt after a non-blocking connect returns -1 if something is
+	 * really messed up (bad descriptor, usually). Otherwise, it returns 0 and
+	 * error holds what connect would have returned if it blocked until now.
+	 * Thus, error == 0 is success, error == EINPROGRESS means "try again",
+	 * and anything else is a real error.
+	 *
+	 * (error == EINPROGRESS can happen after a select because the kernel can
+	 * be overly optimistic sometimes. select is just a hint that you might be
+	 * able to do something.)
+	 */
+	len = sizeof(error);
+	ret = getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len);
+	if (ret == 0 && error == EINPROGRESS)
+		return; /* we'll be called again later */
+		
+	purple_input_remove(qd->tx_handler);
+	qd->tx_handler = 0;
+	if (ret < 0 || error != 0) {
+		if(ret != 0) 
+			error = errno;
+
+		close(source);
+
+		purple_debug_error("proxy", "getsockopt SO_ERROR check: %s\n", g_strerror(error));
+
+		qq_connect_cb(gc, -1, _("Unable to connect"));
+		return;
+	}
+
+	qq_connect_cb(gc, source, NULL);
+}
+
+static void udp_host_resolved(GSList *hosts, gpointer data, const char *error_message) {
+	PurpleConnection *gc;
+	qq_data *qd;
+	struct sockaddr server_addr;
+	int addr_size;
+	gint fd = -1;
+	int flags;
+
+	gc = (PurpleConnection *) data;
+	g_return_if_fail(gc != NULL && gc->proto_data != NULL);
+
+	qd = (qq_data *) gc->proto_data;
+
+	/* udp_query_data must be set as NULL.
+	 * Otherwise purple_dnsquery_destroy in qq_disconnect cause glib double free error */
+	qd->udp_query_data = NULL;
+
+	if (!hosts || !hosts->data) {
+		purple_connection_error_reason(gc,
+			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+			_("Couldn't resolve host"));
+		return;
+	}
+
+	addr_size = GPOINTER_TO_INT(hosts->data);
+	hosts = g_slist_remove(hosts, hosts->data);
+	memcpy(&server_addr, hosts->data, addr_size);
+	g_free(hosts->data);
+	
+	hosts = g_slist_remove(hosts, hosts->data);
+	while(hosts) {
+		hosts = g_slist_remove(hosts, hosts->data);
+		g_free(hosts->data);
+		hosts = g_slist_remove(hosts, hosts->data);
+	}
+
+	fd = socket(PF_INET, SOCK_DGRAM, 0);
+	if (fd < 0) {
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
+				"Unable to create socket: %s\n", g_strerror(errno));
+		return;
+	}
+
+	/* we use non-blocking mode to speed up connection */
+	flags = fcntl(fd, F_GETFL);
+	fcntl(fd, F_SETFL, flags | O_NONBLOCK);
+
+	/* From Unix-socket-FAQ: http://www.faqs.org/faqs/unix-faq/socket/
+	 *
+	 * If a UDP socket is unconnected, which is the normal state after a
+	 * bind() call, then send() or write() are not allowed, since no
+	 * destination is available; only sendto() can be used to send data.
+	 *   
+	 * Calling connect() on the socket simply records the specified address
+	 * and port number as being the desired communications partner. That
+	 * means that send() or write() are now allowed; they use the destination
+	 * address and port given on the connect call as the destination of packets.
+	 */
+	if (connect(fd, &server_addr, addr_size) >= 0) {
+		purple_debug(PURPLE_DEBUG_INFO, "QQ", "Connected.\n");
+		flags = fcntl(fd, F_GETFL);
+		fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
+		qq_connect_cb(gc, fd, NULL);
+		return;
+	}
+	
+	/* [EINPROGRESS]
+	 *    The socket is marked as non-blocking and the connection cannot be 
+	 *    completed immediately. It is possible to select for completion by 
+	 *    selecting the socket for writing.
+	 * [EINTR]
+	 *    A signal interrupted the call. 
+	 *    The connection is established asynchronously.
+	 */
+	if ((errno == EINPROGRESS) || (errno == EINTR)) {
+			purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Connect in asynchronous mode.\n");
+			qd->tx_handler = purple_input_add(fd, PURPLE_INPUT_WRITE, udp_can_write, gc);
+			return;
+		}
+
+	purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Connection failed: %d\n", g_strerror(errno));
+	close(fd);
+}
+
+/* establish a generic QQ connection 
+ * TCP/UDP, and direct/redirected */
+void qq_connect(PurpleAccount *account)
+{
+	PurpleConnection *gc;
+	qq_data *qd;
+	gchar *conn_msg;
+
+	gc = purple_account_get_connection(account);
+	g_return_if_fail(gc != NULL && gc->proto_data != NULL);
+
+	qd = (qq_data *) gc->proto_data;
+
+
+	/* test set_new_server
+	while (set_new_server(qd)) {
+   		purple_debug(PURPLE_DEBUG_INFO, "QQ_TEST",
+   			"New server %s:%d  Real server %s:%d\n",
+   			qd->server_name, qd->user_port, qd->real_hostname, qd->real_port);
+	}
+	purple_debug(PURPLE_DEBUG_INFO, "QQ_TEST", "qd->servers %lu\n",
+ 			qd->servers);
+ 	exit(1);
+	*/
+	if (qd->server_name == NULL) {
+		/* must be first call this function */
+		if ( set_new_server(qd) != TRUE) {
+			purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+					_("Failed to connect server"));
+			return;
+		}
+	}
+
+	if (qd->real_hostname == NULL || qd->real_port == 0) {
+		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+				_("hostname is NULL or port is 0"));
+		return;
+	}
+
+	conn_msg = g_strdup_printf( _("Connecting server %s, retries %d"),
+		qd->real_hostname, qd->reconnect_times);
+	purple_connection_update_progress(gc, conn_msg, 1, QQ_CONNECT_STEPS);
+	g_free(conn_msg);
+
+	if (qd->is_redirect) {
+   		purple_debug(PURPLE_DEBUG_INFO, "QQ", "Redirect to %s:%d\n",
+   			qd->real_hostname, qd->real_port);
+   	}
+	qd->is_redirect = FALSE;
+
+	qd->fd = -1;
+	qd->tx_handler = 0;
+	
+	/* QQ connection via UDP/TCP. 
+	* Now use Purple proxy function to provide TCP proxy support,
+	* and qq_udp_proxy.c to add UDP proxy support (thanks henry) */
+	if(qd->use_tcp) {
+   		purple_debug(PURPLE_DEBUG_INFO, "QQ", "TCP Connect to %s:%d\n",
+   			qd->real_hostname, qd->real_port);
+
+		/* TODO: is there a good default grow size? */
+		purple_debug(PURPLE_DEBUG_INFO, "QQ", "Create tcp_txbuf\n");
+		qd->tcp_txbuf = purple_circ_buffer_new(0);
+
+		qd->connect_data = purple_proxy_connect(NULL, account,
+				qd->real_hostname, qd->real_port, qq_connect_cb, gc);
+		if (qd->connect_data == NULL) {
+			purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+				_("Unable to connect."));
+		}
+		return;
+	}
+	
+	purple_debug(PURPLE_DEBUG_INFO, "QQ", "UDP Connect to %s:%d\n",
+		qd->real_hostname, qd->real_port);
+
+	g_return_if_fail(qd->udp_query_data == NULL);
+	qd->udp_query_data = purple_dnsquery_a(qd->real_hostname, qd->real_port,
+		udp_host_resolved, gc);
+	if (qd->udp_query_data == NULL) {
+		purple_connection_error_reason(qd->gc,
+			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+			_("Could not resolve hostname"));
+	}
+}
+
+/* clean up qq_data structure and all its components
+ * always used before a redirectly connection */
+void qq_disconnect(PurpleConnection *gc)
+{
+	qq_data *qd;
+
+	g_return_if_fail(gc != NULL && gc->proto_data != NULL);
+	qd = (qq_data *) gc->proto_data;
+
+	purple_debug(PURPLE_DEBUG_INFO, "QQ", "Disconnecting ...\n");
+	/* finish  all I/O */
+	if (qd->fd >= 0 && qd->logged_in) {
+		qq_send_packet_logout(gc);
+	}
+
+	if (qd->resend_timeout > 0) {
+		purple_timeout_remove(qd->resend_timeout);
+		qd->resend_timeout = 0;
+	}
+
+	if (gc->inpa > 0) {
+		purple_input_remove(gc->inpa);
+		gc->inpa = 0;
+	}
+
+	if (qd->fd >= 0) {
+		close(qd->fd);
+		qd->fd = -1;
+	}
+
+	if (qd->reconnect_timeout > 0) {
+		purple_timeout_remove(qd->reconnect_timeout);
+		qd->reconnect_timeout = 0;
+	}
+
+	if (qd->connect_data != NULL) {
+		purple_debug(PURPLE_DEBUG_INFO, "QQ", "Cancel connect_data\n");
+		purple_proxy_connect_cancel(qd->connect_data);
+	}
+	
+	if(qd->tcp_txbuf != NULL) {
+		purple_debug(PURPLE_DEBUG_INFO, "QQ", "destroy tcp_txbuf\n");
+		purple_circ_buffer_destroy(qd->tcp_txbuf);
+		qd->tcp_txbuf = NULL;
+	}
+	
+	if (qd->tx_handler) {
+		purple_input_remove(qd->tx_handler);
+		qd->tx_handler = 0;
+	}
+	if (qd->tcp_rxqueue != NULL) {
+		purple_debug(PURPLE_DEBUG_INFO, "QQ", "destroy tcp_rxqueue\n");
+		g_free(qd->tcp_rxqueue);
+		qd->tcp_rxqueue = NULL;
+		qd->tcp_rxlen = 0;
+	}
+	
+	if (qd->udp_query_data != NULL) {
+		purple_debug(PURPLE_DEBUG_INFO, "QQ", "destroy udp_query_data\n");
+		purple_dnsquery_destroy(qd->udp_query_data);
+		qd->udp_query_data = NULL;
+	}
+
+	memset(qd->rcv_window, 0, sizeof(qd->rcv_window));
+	qq_rcv_trans_remove_all(qd);
+	qq_send_trans_remove_all(qd);
+	
+	if (qd->inikey) {
+		purple_debug(PURPLE_DEBUG_INFO, "QQ", "free inikey\n");
+		g_free(qd->inikey);
+		qd->inikey = NULL;
+	}
+	if (qd->pwkey) {
+		purple_debug(PURPLE_DEBUG_INFO, "QQ", "free pwkey\n");
+		g_free(qd->pwkey);
+		qd->pwkey = NULL;
+	}
+	if (qd->session_key) {
+		purple_debug(PURPLE_DEBUG_INFO, "QQ", "free session_key\n");
+		g_free(qd->session_key);
+		qd->session_key = NULL;
+	}
+	if (qd->session_md5) {
+		purple_debug(PURPLE_DEBUG_INFO, "QQ", "free session_md5\n");
+		g_free(qd->session_md5);
+		qd->session_md5 = NULL;
+	}
+	if (qd->my_ip) {
+		purple_debug(PURPLE_DEBUG_INFO, "QQ", "free my_ip\n");
+		g_free(qd->my_ip);
+		qd->my_ip = NULL;
+	}
+
+	qq_group_packets_free(qd);
+	qq_group_free_all(qd);
+	qq_add_buddy_request_free(qd);
+	qq_info_query_free(qd);
+	qq_buddies_list_free(gc->account, qd);
+}
+
+static gint encap(qq_data *qd, guint8 *buf, gint maxlen, guint16 cmd, guint16 seq, 
+	guint8 *data, gint data_len)
+{
+	gint bytes = 0;
+	g_return_val_if_fail(qd != NULL && buf != NULL && maxlen > 0, -1);
+	
+	if (data == NULL) {
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Fail encap packet, data is NULL\n");
+		return -1;
+	}
+	if (data_len <= 0) {
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Fail encap packet, data len <= 0\n");
+		return -1;
+	}
+
+	/* QQ TCP packet has two bytes in the begining defines packet length
+	 * so leave room here to store packet size */
+	if (qd->use_tcp) {
+		bytes += qq_put16(buf + bytes, 0x0000);
+	}
+	/* now comes the normal QQ packet as UDP */
+	bytes += qq_put8(buf + bytes, QQ_PACKET_TAG);
+	bytes += qq_put16(buf + bytes, QQ_CLIENT);
+	bytes += qq_put16(buf + bytes, cmd);
+	
+	bytes += qq_put16(buf + bytes, seq);
+
+	bytes += qq_put32(buf + bytes, qd->uid);
+	bytes += qq_putdata(buf + bytes, data, data_len);
+	bytes += qq_put8(buf + bytes, QQ_PACKET_TAIL);
+
+	/* set TCP packet length at begin of the packet */
+	if (qd->use_tcp) {
+		qq_put16(buf, bytes);
+	}
+
+	return bytes;
+}
+
+gint qq_send_data(qq_data *qd, guint16 cmd, guint8 *data, gint data_len)
+{
+	guint8 *buf;
+	gint buf_len;
+	gint bytes_sent;
+	gint seq;
+
+	g_return_val_if_fail(qd != NULL, -1);
+	g_return_val_if_fail(data != NULL && data_len > 0, -1);
+
+	buf = g_newa(guint8, MAX_PACKET_SIZE);
+	memset(buf, 0, MAX_PACKET_SIZE);
+	seq = ++(qd->send_seq);
+	buf_len = encap(qd, buf, MAX_PACKET_SIZE, cmd, seq, data, data_len);
+	if (buf_len <= 0) {
+		return -1;
+	}
+
+	if (qd->use_tcp) {
+		bytes_sent = tcp_send_out(qd, buf, buf_len);
+	} else {
+		bytes_sent = udp_send_out(qd, buf, buf_len);
+	}
+
+	/* always need ack */
+	qq_send_trans_append(qd, buf, buf_len, cmd, seq);
+
+	if (QQ_DEBUG) {
+		qq_show_packet("QQ_SEND_DATA", buf, buf_len);
+		purple_debug(PURPLE_DEBUG_INFO, "QQ",
+				"<== [%05d], %s, total %d bytes is sent %d\n", 
+				seq, qq_get_cmd_desc(cmd), buf_len, bytes_sent);
+	}
+	return bytes_sent;
+}
+
+/* send the packet generated with the given cmd and data
+ * return the number of bytes sent to socket if succeeds
+ * return -1 if there is any error */
+gint qq_send_cmd_detail(qq_data *qd, guint16 cmd, guint16 seq, gboolean need_ack,
+	guint8 *data, gint data_len)
+{
+	guint8 *buf;
+	gint buf_len;
+	guint8 *encrypted_data;
+	gint encrypted_len;
+	gint bytes_sent;
+
+	g_return_val_if_fail(qd != NULL && qd->session_key != NULL, -1);
+	g_return_val_if_fail(data != NULL && data_len > 0, -1);
+
+	encrypted_len = data_len + 16;	/* at most 16 bytes more */
+	encrypted_data = g_newa(guint8, encrypted_len);
+
+	qq_encrypt(data, data_len, qd->session_key, encrypted_data, &encrypted_len);
+
+	buf = g_newa(guint8, MAX_PACKET_SIZE);
+	memset(buf, 0, MAX_PACKET_SIZE);
+	buf_len = encap(qd, buf, MAX_PACKET_SIZE, cmd, seq, encrypted_data, encrypted_len);
+	if (buf_len <= 0) {
+		return -1;
+	}
+
+	if (QQ_DEBUG) {
+		qq_show_packet("QQ_SEND_CMD", buf, buf_len);
+	}
+	if (qd->use_tcp) {
+		bytes_sent = tcp_send_out(qd, buf, buf_len);
+	} else {
+		bytes_sent = udp_send_out(qd, buf, buf_len);
+	}
+	
+	/* if it does not need ACK, we send ACK manually several times */
+	if (need_ack)  {
+		qq_send_trans_append(qd, buf, buf_len, cmd, seq);
+	}
+
+	if (QQ_DEBUG) {
+		qq_show_packet("QQ_SEND_CMD", buf, buf_len);
+		purple_debug(PURPLE_DEBUG_INFO, "QQ",
+				"<== [%05d], %s, total %d bytes is sent %d\n", 
+				seq, qq_get_cmd_desc(cmd), buf_len, bytes_sent);
+	}
+	return bytes_sent;
+}
+
+gint qq_send_cmd(qq_data *qd, guint16 cmd, guint8 *data, gint data_len)
+{
+	g_return_val_if_fail(qd != NULL, -1);
+	g_return_val_if_fail(data != NULL && data_len > 0, -1);
+
+	qd->send_seq++;
+	return qq_send_cmd_detail(qd, cmd, qd->send_seq, TRUE, data, data_len);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/qq/qq_network.h	Wed Jul 09 00:32:18 2008 +0000
@@ -0,0 +1,44 @@
+/**
+ * @file qq_network.h
+ *
+ * purple
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
+ */
+
+#ifndef _QQ_PROXY_H
+#define _QQ_PROXY_H
+
+#include <glib.h>
+#include "connection.h"
+
+#include "qq.h"
+
+#define QQ_CONNECT_STEPS    3	/* steps in connection */
+
+void qq_connect(PurpleAccount *account);
+void qq_disconnect(PurpleConnection *gc);
+void qq_connect_later(PurpleConnection *gc);
+
+gint qq_send_data(qq_data *qd, guint16 cmd, guint8 *data, gint datalen);
+gint qq_send_cmd(qq_data *qd, guint16 cmd, guint8 *data, gint datalen);
+gint qq_send_cmd_detail(qq_data *qd, guint16 cmd, guint16 seq, gboolean need_ack,
+	guint8 *data, gint data_len);
+
+#endif
--- a/libpurple/protocols/qq/qq_proxy.c	Wed Jul 09 00:27:44 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,529 +0,0 @@
-/**
- * @file qq_proxy.c
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-
-#include "cipher.h"
-#include "debug.h"
-#include "internal.h"
-
-#ifdef _WIN32
-#define random rand
-#define srandom srand
-#endif
-
-#include "packet_parse.h"
-#include "buddy_info.h"
-#include "buddy_opt.h"
-#include "char_conv.h"
-#include "group_free.h"
-#include "login_logout.h"
-#include "qq_proxy.h"
-#include "recv_core.h"
-#include "send_core.h"
-#include "sendqueue.h"
-#include "udp_proxy_s5.h"
-#include "utils.h"
-
-/* These functions are used only in development phase */
-/*
-static void _qq_show_socket(gchar *desc, gint fd) {
-	struct sockaddr_in sin;
-	socklen_t len = sizeof(sin);
-	getsockname(fd, (struct sockaddr *)&sin, &len);
-	purple_debug(PURPLE_DEBUG_INFO, desc, "%s:%d\n",
-            inet_ntoa(sin.sin_addr), g_ntohs(sin.sin_port));
-}
-*/
-
-void _qq_show_packet(const gchar *desc, const guint8 *buf, gint len)
-{
-	char buf1[8*len+2], buf2[10];
-	int i;
-	buf1[0] = 0;
-	for (i = 0; i < len; i++) {
-		sprintf(buf2, " %02x(%d)", buf[i] & 0xff, buf[i] & 0xff);
-		strcat(buf1, buf2);
-	}
-	strcat(buf1, "\n");
-	purple_debug(PURPLE_DEBUG_INFO, desc, "%s", buf1);
-}
-
-/* QQ 2003iii uses double MD5 for the pwkey to get the session key */
-static guint8 *_gen_pwkey(const gchar *pwd)
-{
-        PurpleCipher *cipher;
-        PurpleCipherContext *context;
-
-	guchar pwkey_tmp[QQ_KEY_LENGTH];
-
-	cipher = purple_ciphers_find_cipher("md5");
-	context = purple_cipher_context_new(cipher, NULL);
-	purple_cipher_context_append(context, (guchar *) pwd, strlen(pwd));
-	purple_cipher_context_digest(context, sizeof(pwkey_tmp), pwkey_tmp, NULL);
-	purple_cipher_context_destroy(context);
-	context = purple_cipher_context_new(cipher, NULL);
-	purple_cipher_context_append(context, pwkey_tmp, QQ_KEY_LENGTH);
-	purple_cipher_context_digest(context, sizeof(pwkey_tmp), pwkey_tmp, NULL);
-	purple_cipher_context_destroy(context);
-
-	return g_memdup(pwkey_tmp, QQ_KEY_LENGTH);
-}
-
-static gboolean _qq_fill_host(GSList *hosts, struct sockaddr_in *addr, gint *addr_size)
-{
-	if (!hosts || !hosts->data)
-		return FALSE;
-
-	*addr_size = GPOINTER_TO_INT(hosts->data);
-
-	hosts = g_slist_remove(hosts, hosts->data);
-	memcpy(addr, hosts->data, *addr_size);
-	g_free(hosts->data);
-	hosts = g_slist_remove(hosts, hosts->data);
-	while(hosts) {
-		hosts = g_slist_remove(hosts, hosts->data);
-		g_free(hosts->data);
-		hosts = g_slist_remove(hosts, hosts->data);
-	}
-
-	return TRUE;
-}
-
-/* set up any finalizing start-up stuff */
-static void _qq_start_services(PurpleConnection *gc)
-{
-	/* start watching for IMs about to be sent */
-	/*
-	purple_signal_connect(purple_conversations_get_handle(),
-			"sending-im-msg", gc,
-			PURPLE_CALLBACK(qq_sending_im_msg_cb), NULL);
-			*/
-}
-
-/* the callback function after socket is built
- * we setup the qq protocol related configuration here */
-static void _qq_got_login(gpointer data, gint source, const gchar *error_message)
-{
-	qq_data *qd;
-	PurpleConnection *gc;
-	gchar *buf;
-	const gchar *passwd;
-
-	gc = (PurpleConnection *) data;
-
-	if (!PURPLE_CONNECTION_IS_VALID(gc)) {
-		close(source);
-		return;
-	}
-
-	g_return_if_fail(gc != NULL && gc->proto_data != NULL);
-
-	if (source < 0) {	/* socket returns -1 */
-		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_message);
-		return;
-	}
-
-	qd = (qq_data *) gc->proto_data;
-
-	/*
-	_qq_show_socket("Got login socket", source);
-	*/
-
-	/* QQ use random seq, to minimize duplicated packets */
-	srandom(time(NULL));
-	qd->send_seq = random() & 0x0000ffff;
-	qd->fd = source;
-	qd->logged_in = FALSE;
-	qd->channel = 1;
-	qd->uid = strtol(purple_account_get_username(purple_connection_get_account(gc)), NULL, 10);
-
-	/* now generate md5 processed passwd */
-	passwd = purple_account_get_password(purple_connection_get_account(gc));
-	qd->pwkey = _gen_pwkey(passwd);
-
-	qd->sendqueue_timeout = purple_timeout_add(QQ_SENDQUEUE_TIMEOUT, qq_sendqueue_timeout_callback, gc);
-	gc->inpa = purple_input_add(qd->fd, PURPLE_INPUT_READ, qq_input_pending, gc);
-
-	/* Update the login progress status display */
-	buf = g_strdup_printf("Login as %d", qd->uid);
-	purple_connection_update_progress(gc, buf, 1, QQ_CONNECT_STEPS);
-	g_free(buf);
-
-	_qq_start_services(gc);
-
-	qq_send_packet_request_login_token(gc);
-}
-
-/* clean up qq_data structure and all its components
- * always used before a redirectly connection */
-static void _qq_common_clean(PurpleConnection *gc)
-{
-	qq_data *qd;
-
-	g_return_if_fail(gc != NULL && gc->proto_data != NULL);
-	qd = (qq_data *) gc->proto_data;
-
-	/* finish  all I/O */
-	if (qd->fd >= 0 && qd->logged_in)
-		qq_send_packet_logout(gc);
-	close(qd->fd);
-
-	if (qd->sendqueue_timeout > 0) {
-		purple_timeout_remove(qd->sendqueue_timeout);
-		qd->sendqueue_timeout = 0;
-	}
-
-	if (gc->inpa > 0) {
-		purple_input_remove(gc->inpa);
-		gc->inpa = 0;
-	}
-
-	qq_b4_packets_free(qd);
-	qq_sendqueue_free(qd);
-	qq_group_packets_free(qd);
-	qq_group_free_all(qd);
-	qq_add_buddy_request_free(qd);
-	qq_info_query_free(qd);
-	qq_buddies_list_free(gc->account, qd);
-}
-
-static void no_one_calls(gpointer data, gint source, PurpleInputCondition cond)
-{
-        struct PHB *phb = data;
-	socklen_t len;
-	int error=0, ret;
-
-	purple_debug_info("proxy", "Connected.\n");
-
-	len = sizeof(error);
-
-	/*
-	* getsockopt after a non-blocking connect returns -1 if something is
-	* really messed up (bad descriptor, usually). Otherwise, it returns 0 and
-	* error holds what connect would have returned if it blocked until now.
-	* Thus, error == 0 is success, error == EINPROGRESS means "try again",
-	* and anything else is a real error.
-	*
-	* (error == EINPROGRESS can happen after a select because the kernel can
-	* be overly optimistic sometimes. select is just a hint that you might be
-	* able to do something.)
-	*/
-	ret = getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len);
-	if (ret == 0 && error == EINPROGRESS)
-		return; /* we'll be called again later */
-	if (ret < 0 || error != 0) {
-		if(ret!=0) 
-			error = errno;
-		close(source);
-		purple_input_remove(phb->inpa);
-
-		purple_debug_error("proxy", "getsockopt SO_ERROR check: %s\n", g_strerror(error));
-
-		phb->func(phb->data, -1, _("Unable to connect"));
-		return;
-	}
-
-	purple_input_remove(phb->inpa);
-
-	if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) {
-
-		phb->func(phb->data, source, NULL);
-	}
-
-	g_free(phb->host);
-	g_free(phb);
-}
-
-/* returns -1 if fails, otherwise returns the file handle */
-static gint _qq_proxy_none(struct PHB *phb, struct sockaddr *addr, socklen_t addrlen)
-{
-	gint fd = -1;
-	int flags;
-
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "Using UDP without proxy\n");
-	fd = socket(PF_INET, SOCK_DGRAM, 0);
-
-	if (fd < 0) {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ Redirect", 
-			"Unable to create socket: %s\n", g_strerror(errno));
-		return -1;
-	}
-
-	/* we use non-blocking mode to speed up connection */
-	flags = fcntl(fd, F_GETFL);
-	fcntl(fd, F_SETFL, flags | O_NONBLOCK);
-
-	/* From Unix-socket-FAQ: http://www.faqs.org/faqs/unix-faq/socket/
-	 *
-	 * If a UDP socket is unconnected, which is the normal state after a
-	 * bind() call, then send() or write() are not allowed, since no
-	 * destination is available; only sendto() can be used to send data.
-	 *   
-	 * Calling connect() on the socket simply records the specified address
-	 * and port number as being the desired communications partner. That
-	 * means that send() or write() are now allowed; they use the destination
-	 * address and port given on the connect call as the destination of packets.
-	 */
-	if (connect(fd, addr, addrlen) < 0) {
-		/* [EINPROGRESS]
-		 *    The socket is marked as non-blocking and the connection cannot be 
-		 *    completed immediately. It is possible to select for completion by 
-		 *    selecting the socket for writing.
-		 * [EINTR]
-		 *    A signal interrupted the call. 
-		 *    The connection is established asynchronously.
-		 */
-		if ((errno == EINPROGRESS) || (errno == EINTR)) {
-			purple_debug_warning("QQ", "Connect in asynchronous mode.\n");
-			phb->inpa = purple_input_add(fd, PURPLE_INPUT_WRITE, no_one_calls, phb);
-		} else {
-			purple_debug_error("QQ", "Connection failed: %s\n", g_strerror(errno));
-			close(fd);
-			return -1;
-		}		/* if errno */
-	} else {		/* connect returns 0 */
-		purple_debug(PURPLE_DEBUG_INFO, "QQ", "Connected.\n");
-		flags = fcntl(fd, F_GETFL);
-		fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
-		phb->func(phb->data, fd, NULL);
-	}
-
-	return fd;
-}
-
-static void _qq_proxy_resolved(GSList *hosts, gpointer data, const char *error_message)
-{
-	struct PHB *phb = (struct PHB *) data;
-	struct sockaddr_in addr;
-	gint addr_size, ret = -1;
-
-	if(_qq_fill_host(hosts, &addr, &addr_size))
-		ret = qq_proxy_socks5(phb, (struct sockaddr *) &addr, addr_size);
-
-	if (ret < 0) {
-		phb->func(phb->data, -1, _("Unable to connect"));
-		g_free(phb->host);
-		g_free(phb);
-	}
-}
-
-static void _qq_server_resolved(GSList *hosts, gpointer data, const char *error_message)
-{
-	struct PHB *phb = (struct PHB *) data;
-	PurpleConnection *gc = (PurpleConnection *) phb->data;
-	qq_data *qd = (qq_data *) gc->proto_data;
-	struct sockaddr_in addr;
-	gint addr_size, ret = -1;
-
-	if(_qq_fill_host(hosts, &addr, &addr_size)) {
-		switch (purple_proxy_info_get_type(phb->gpi)) {
-			case PURPLE_PROXY_NONE:
-				ret = _qq_proxy_none(phb, (struct sockaddr *) &addr, addr_size);
-				break;
-			case PURPLE_PROXY_SOCKS5:
-				ret = 0;
-				if (purple_proxy_info_get_host(phb->gpi) == NULL || 
-						purple_proxy_info_get_port(phb->gpi) == 0) {
-					purple_debug(PURPLE_DEBUG_ERROR, "QQ", 
-							"Use of socks5 proxy selected but host or port info doesn't exist.\n");
-					ret = -1;
-				} else {
-					/* as the destination is always QQ server during the session, 
-				 	* we can set dest_sin here, instead of _qq_s5_canread_again */
-					memcpy(&qd->dest_sin, &addr, addr_size);
-					if (purple_dnsquery_a(purple_proxy_info_get_host(phb->gpi),
-							purple_proxy_info_get_port(phb->gpi),
-							_qq_proxy_resolved, phb) == NULL)
-						ret = -1;
-				}
-				break;
-			default:
-				purple_debug(PURPLE_DEBUG_WARNING, "QQ", 
-						"Proxy type %i is unsupported, not using a proxy.\n",
-						purple_proxy_info_get_type(phb->gpi));
-				ret = _qq_proxy_none(phb, (struct sockaddr *) &addr, addr_size);
-		}
-	}
-
-	if (ret < 0) {
-		phb->func(gc, -1, _("Unable to connect"));
-		g_free(phb->host);
-		g_free(phb);
-	}
-}
-
-/* returns -1 if dns lookup fails, otherwise returns 0 */
-static gint _qq_udp_proxy_connect(PurpleAccount *account,
-			   const gchar *server, guint16 port, 
-			   void callback(gpointer, gint, const gchar *error_message), 
-			   PurpleConnection *gc)
-{
-	PurpleProxyInfo *info;
-	struct PHB *phb;
-	qq_data *qd = (qq_data *) gc->proto_data;
-
-	g_return_val_if_fail(gc != NULL && qd != NULL, -1);
-
-	info = purple_proxy_get_setup(account);
-
-	phb = g_new0(struct PHB, 1);
-	phb->host = g_strdup(server);
-	phb->port = port;
-	phb->account = account;
-	phb->gpi = info;
-	phb->func = callback;
-	phb->data = gc;
-	qd->proxy_type = purple_proxy_info_get_type(phb->gpi);
-
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "Choosing proxy type %d\n", 
-			purple_proxy_info_get_type(phb->gpi));
-
-	if (purple_dnsquery_a(server, port, _qq_server_resolved, phb) == NULL) {
-		phb->func(gc, -1, _("Unable to connect"));
-		g_free(phb->host);
-		g_free(phb);
-		return -1;
-	} else {
-		return 0;
-	}
-}
-
-/* QQ connection via UDP/TCP. 
- * I use Purple proxy function to provide TCP proxy support,
- * and qq_udp_proxy.c to add UDP proxy support (thanks henry) */
-static gint _proxy_connect_full (PurpleAccount *account, const gchar *host, guint16 port, 
-		PurpleProxyConnectFunction func, gpointer data, gboolean use_tcp)
-{
-	PurpleConnection *gc;
-	qq_data *qd;
-
-	gc = purple_account_get_connection(account);
-	qd = (qq_data *) gc->proto_data;
-	qd->server_ip = g_strdup(host);
-	qd->server_port = port;
-
-	if(use_tcp)
-		return (purple_proxy_connect(NULL, account, host, port, func, data) == NULL);
-	else
-		return _qq_udp_proxy_connect(account, host, port, func, data);
-}
-
-/* establish a generic QQ connection 
- * TCP/UDP, and direct/redirected */
-gint qq_connect(PurpleAccount *account, const gchar *host, guint16 port, 
-		gboolean use_tcp, gboolean is_redirect)
-{
-	PurpleConnection *gc;
-	qq_data *qd;
-
-	g_return_val_if_fail(host != NULL, -1);
-	g_return_val_if_fail(port > 0, -1);
-
-	gc = purple_account_get_connection(account);
-	g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, -1);
-
-	if (is_redirect)
-		_qq_common_clean(gc);
-
-	qd = (qq_data *) gc->proto_data;
-	qd->before_login_packets = g_queue_new();
-
-	return _proxy_connect_full(account, host, port, _qq_got_login, gc, use_tcp);
-}
-
-/* clean up the given QQ connection and free all resources */
-void qq_disconnect(PurpleConnection *gc)
-{
-	qq_data *qd;
-
-	g_return_if_fail(gc != NULL);
-
-	_qq_common_clean(gc);
-
-	qd = gc->proto_data;
-	g_free(qd->inikey);
-	g_free(qd->pwkey);
-	g_free(qd->session_key);
-	g_free(qd->session_md5);
-	g_free(qd->my_ip);
-	g_free(qd);
-
-	gc->proto_data = NULL;
-}
-
-/* send packet with proxy support */
-gint qq_proxy_write(qq_data *qd, guint8 *data, gint len)
-{
-	guint8 *buf;
-	gint ret;
-
-	g_return_val_if_fail(qd != NULL && qd->fd >= 0 && data != NULL && len > 0, -1);
-
-	/* TCP sock5 may be processed twice
-	 * so we need to check qd->use_tcp as well */
-	if ((!qd->use_tcp) && qd->proxy_type == PURPLE_PROXY_SOCKS5) {	/* UDP sock5 */
-		buf = g_newa(guint8, len + 10);
-		buf[0] = 0x00;
-		buf[1] = 0x00;	/* reserved */
-		buf[2] = 0x00;	/* frag */
-		buf[3] = 0x01;	/* type */
-		g_memmove(buf + 4, &(qd->dest_sin.sin_addr.s_addr), 4);
-		g_memmove(buf + 8, &(qd->dest_sin.sin_port), 2);
-		g_memmove(buf + 10, data, len);
-		errno = 0;
-		ret = send(qd->fd, buf, len + 10, 0);
-	} else {
-		errno = 0;
-		ret = send(qd->fd, data, len, 0);
-	}
-	if (ret == -1)
-		purple_connection_error_reason(qd->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, g_strerror(errno));
-
-	return ret;
-}
-
-/* read packet input with proxy support */
-gint qq_proxy_read(qq_data *qd, guint8 *data, gint len)
-{
-	guint8 *buf;
-	gint bytes;
-	buf = g_newa(guint8, MAX_PACKET_SIZE + 10);
-
-	g_return_val_if_fail(qd != NULL && data != NULL && len > 0, -1);
-	g_return_val_if_fail(qd->fd > 0, -1);
-
-	bytes = read(qd->fd, buf, len + 10);
-	if (bytes < 0)
-		return -1;
-
-	if ((!qd->use_tcp) && qd->proxy_type == PURPLE_PROXY_SOCKS5) {	/* UDP sock5 */
-		if (bytes < 10)
-			return -1;
-		bytes -= 10;
-		g_memmove(data, buf + 10, bytes);	/* cut off the header */
-	} else {
-		g_memmove(data, buf, bytes);
-	}
-
-	return bytes;
-}
--- a/libpurple/protocols/qq/qq_proxy.h	Wed Jul 09 00:27:44 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,56 +0,0 @@
-/**
- * @file qq_proxy.h
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-
-#ifndef _QQ_PROXY_H
-#define _QQ_PROXY_H
-
-#include <glib.h>
-#include "dnsquery.h"
-#include "proxy.h"
-
-#include "qq.h"
-
-#define QQ_CONNECT_STEPS    2	/* steps in connection */
-
-struct PHB {
-	PurpleProxyConnectFunction func;
-	gpointer data;
-	gchar *host;
-	gint port;
-	gint inpa;
-	PurpleProxyInfo *gpi;
-	PurpleAccount *account;
-	gint udpsock;
-	gpointer sockbuf;
-};
-
-gint qq_proxy_read(qq_data *qd, guint8 *data, gint len);
-gint qq_proxy_write(qq_data *qd, guint8 *data, gint len);
-
-gint qq_connect(PurpleAccount *account, const gchar *host, guint16 port, gboolean use_tcp, gboolean is_redirect);
-void qq_disconnect(PurpleConnection *gc);
-
-void _qq_show_packet(const gchar *desc, const guint8 *buf, gint len);
-
-#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/qq/qq_trans.c	Wed Jul 09 00:32:18 2008 +0000
@@ -0,0 +1,246 @@
+/**
+ * @file qq_trans.c
+ *
+ * purple
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
+ */
+
+#include "internal.h"
+
+#include "connection.h"
+#include "debug.h"
+#include "notify.h"
+#include "prefs.h"
+#include "request.h"
+
+#include "header_info.h"
+#include "qq_network.h"
+#include "qq_trans.h"
+
+#define QQ_RESEND_MAX               8	/* max resend per packet */
+
+typedef struct _transaction {
+	guint16 seq;
+	guint16 cmd;
+	guint8 *buf;
+	gint buf_len;
+
+	gint fd;
+	gint retries;
+	time_t create_time;
+} transaction;
+
+void qq_send_trans_append(qq_data *qd, guint8 *buf, gint buf_len, guint16 cmd, guint16 seq)
+{
+	transaction *trans = g_new0(transaction, 1);
+
+	g_return_if_fail(trans != NULL);
+
+	trans->fd = qd->fd;
+	trans->cmd = cmd;
+	trans->seq = seq;
+	trans->retries = QQ_RESEND_MAX;
+	trans->create_time = time(NULL);
+	trans->buf = g_memdup(buf, buf_len);	/* don't use g_strdup, may have 0x00 */
+	trans->buf_len = buf_len;
+
+	purple_debug(PURPLE_DEBUG_ERROR, "QQ",
+			"Add to transaction, seq = %d, buf = %p, len = %d\n",
+			trans->seq, trans->buf, trans->buf_len);
+	qd->send_trans = g_list_append(qd->send_trans, trans);
+}
+
+/* Remove a packet with seq from send trans */
+void qq_send_trans_remove(qq_data *qd, gpointer data) 
+{
+	transaction *trans = (transaction *)data;
+
+	g_return_if_fail(qd != NULL && data != NULL);
+	
+	purple_debug(PURPLE_DEBUG_INFO, "QQ",
+				"ack [%05d] %s, remove from send tranactions\n",
+				trans->seq, qq_get_cmd_desc(trans->cmd));
+
+	if (trans->buf)	g_free(trans->buf);
+	qd->send_trans = g_list_remove(qd->send_trans, trans);
+	g_free(trans);
+}
+
+gpointer qq_send_trans_find(qq_data *qd, guint16 seq)
+{
+	GList *curr;
+	GList *next;
+	transaction *trans;
+
+	curr = qd->send_trans;
+	while(curr) {
+		next = curr->next;
+		trans = (transaction *) (curr->data);
+		if(trans->seq == seq) {
+			return trans;
+		}
+		curr = next;
+	}
+
+	return NULL;
+}
+
+/* clean up send trans and free all contents */
+void qq_send_trans_remove_all(qq_data *qd)
+{
+	GList *curr;
+	GList *next;
+	transaction *trans;
+	gint count = 0;
+
+	curr = qd->send_trans;
+	while(curr) {
+		next = curr->next;
+		
+		trans = (transaction *) (curr->data);
+		/*
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
+			"Remove to transaction, seq = %d, buf = %p, len = %d\n",
+			trans->seq, trans->buf, trans->len);
+		*/
+		qq_send_trans_remove(qd, trans);
+
+		count++;
+		curr = next;
+	}
+	g_list_free(qd->send_trans);
+
+	purple_debug(PURPLE_DEBUG_INFO, "QQ", "%d packets in send tranactions are freed!\n", count);
+}
+
+gint qq_send_trans_scan(qq_data *qd, gint *start,
+	guint8 *buf, gint maxlen, guint16 *cmd, gint *retries)
+{
+	GList *curr;
+	GList *next = NULL;
+	transaction *trans;
+	gint copylen;
+
+	g_return_val_if_fail(qd != NULL && *start >= 0 && maxlen > 0, -1);
+	
+	/* purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Scan from %d\n", *start); */
+	curr = g_list_nth(qd->send_trans, *start);
+	while(curr) {
+		next = curr->next;
+		*start = g_list_position(qd->send_trans, next);
+		
+		trans = (transaction *) (curr->data);
+		if (trans->buf == NULL || trans->buf_len <= 0) {
+			qq_send_trans_remove(qd, trans);
+			curr = next;
+			continue;
+		}
+
+		if (trans->retries < 0) {
+			purple_debug(PURPLE_DEBUG_ERROR, "QQ",
+				"Remove transaction, seq %d, buf %p, len %d, retries %d, next %d\n",
+				trans->seq, trans->buf, trans->buf_len, trans->retries, *start);
+			qq_send_trans_remove(qd, trans);
+			curr = next;
+			continue;
+		}
+
+		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
+				"Resend transaction, seq %d, buf %p, len %d, retries %d, next %d\n",
+				trans->seq, trans->buf, trans->buf_len, trans->retries, *start);
+		copylen = MIN(trans->buf_len, maxlen);
+		g_memmove(buf, trans->buf, copylen);
+
+		*cmd = trans->cmd;
+		*retries = trans->retries;
+		trans->retries--;
+		return copylen;
+	}
+
+	/* purple_debug(PURPLE_DEBUG_INFO, "QQ", "Scan finished\n"); */
+	return -1;
+}
+
+void qq_rcv_trans_push(qq_data *qd, guint16 cmd, guint16 seq, guint8 *data, gint data_len)
+{
+	transaction *trans = g_new0(transaction, 1);
+
+	g_return_if_fail(data != NULL && data_len > 0);
+	g_return_if_fail(trans != NULL);
+
+	trans->cmd = cmd;
+	trans->seq = seq;
+	trans->buf = g_memdup(data, data_len);
+	trans->buf_len = data_len;
+	trans->create_time = time(NULL);
+
+	if (qd->rcv_trans == NULL)
+		qd->rcv_trans = g_queue_new();
+
+	g_queue_push_head(qd->rcv_trans, trans);
+}
+
+gint qq_rcv_trans_pop(qq_data *qd, guint16 *cmd, guint16 *seq, guint8 *data, gint max_len)
+{
+	transaction *trans = NULL;
+	gint copy_len;
+
+	g_return_val_if_fail(data != NULL && max_len > 0, -1);
+
+	if (g_queue_is_empty(qd->rcv_trans)) {
+		return -1;
+	}
+	trans = (transaction *) g_queue_pop_head(qd->rcv_trans);
+	if (trans == NULL) {
+		return 0;
+	}
+	if (trans->buf == NULL || trans->buf_len <= 0) {
+		return 0;
+	}
+
+	copy_len = MIN(max_len, trans->buf_len);
+	g_memmove(data, trans->buf, copy_len);
+	*cmd = trans->cmd;
+	*seq = trans->seq;
+
+	g_free(trans->buf);
+	g_free(trans);
+	return copy_len;
+}
+
+/* clean up the packets before login */
+void qq_rcv_trans_remove_all(qq_data *qd)
+{
+	transaction *trans = NULL;
+	gint count = 0;
+
+	g_return_if_fail(qd != NULL);
+
+	/* now clean up my own data structures */
+	if (qd->rcv_trans != NULL) {
+		while (NULL != (trans = g_queue_pop_tail(qd->rcv_trans))) {
+			g_free(trans->buf);
+			g_free(trans);
+			count++;
+		}
+		g_queue_free(qd->rcv_trans);
+	}
+	purple_debug(PURPLE_DEBUG_INFO, "QQ", "%d packets in receive tranactions are freed!\n", count);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/qq/qq_trans.h	Wed Jul 09 00:32:18 2008 +0000
@@ -0,0 +1,42 @@
+/**
+ * @file qq_trans.h
+ *
+ * purple
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
+ */
+
+#ifndef _QQ_SEND_QUEUE_H_
+#define _QQ_SEND_QUEUE_H_
+
+#include <glib.h>
+#include "qq.h"
+
+void qq_send_trans_append(qq_data *qd, guint8 *buf, gint bus_len, guint16 cmd, guint16 seq);
+void qq_send_trans_remove(qq_data *qd, gpointer data);
+gpointer qq_send_trans_find(qq_data *qd, guint16 seq);
+void qq_send_trans_remove_all(qq_data *qd);
+
+gint qq_send_trans_scan(qq_data *qd, gint *start, guint8 *buf, gint maxlen, guint16 *cmd, gint *retries);
+
+void qq_rcv_trans_push(qq_data *qd, guint16 cmd, guint16 seq, guint8 *data, gint data_len);
+gint qq_rcv_trans_pop(qq_data *qd, guint16 *cmd, guint16* seq, guint8 *data, gint max_len);
+void qq_rcv_trans_remove_all(qq_data *qd);
+
+#endif
--- a/libpurple/protocols/qq/recv_core.c	Wed Jul 09 00:27:44 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,326 +0,0 @@
-/**
- * @file recv_core.c
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-
-#include "debug.h"
-#include "internal.h"
-
-#include "buddy_info.h"
-#include "buddy_list.h"
-#include "buddy_opt.h"
-#include "buddy_status.h"
-#include "char_conv.h"
-#include "crypt.h"
-#include "group_network.h"
-#include "header_info.h"
-#include "keep_alive.h"
-#include "im.h"
-#include "login_logout.h"
-#include "packet_parse.h"
-#include "qq_proxy.h"
-#include "recv_core.h"
-#include "sendqueue.h"
-#include "sys_msg.h"
-#include "utils.h"
-
-typedef struct _packet_before_login packet_before_login;
-typedef struct _qq_recv_msg_header qq_recv_msg_header;
-
-struct _packet_before_login {
-	guint8 *buf;
-	gint len;
-};
-
-struct _qq_recv_msg_header {
-	guint8 header_tag;
-	guint16 source_tag;
-	guint16 cmd;
-	guint16 seq;		/* can be ack_seq or send_seq, depends on cmd */
-};
-
-/* check whether one sequence number is duplicated or not
- * return TRUE if it is duplicated, otherwise FALSE */
-static gboolean _qq_check_packet_set_window(guint16 seq, PurpleConnection *gc)
-{
-	qq_data *qd;
-	guint8 *byte, mask;
-
-	qd = (qq_data *) gc->proto_data;
-	byte = &(qd->window[seq / 8]);
-	mask = (1 << (seq % 8));
-
-	if ((*byte) & mask)
-		return TRUE;	/* check mask */
-	(*byte) |= mask;
-	return FALSE;		/* set mask */
-}
-
-/* default process, decrypt and dump */
-static void _qq_process_packet_default(guint8 *buf, gint buf_len, guint16 cmd, guint16 seq, PurpleConnection *gc)
-{
-	qq_data *qd;
-	guint8 *data;
-	gchar *msg_utf8;
-	gint len;
-
-	g_return_if_fail(buf != NULL && buf_len != 0);
-
-	qd = (qq_data *) gc->proto_data;
-	len = buf_len;
-	data = g_newa(guint8, len);
-	msg_utf8 = NULL;
-
-	_qq_show_packet("Processing unknown packet", buf, len);
-	if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
-		gchar *hex_dump = hex_dump_to_str(data, len);
-		purple_debug(PURPLE_DEBUG_WARNING, "QQ",
-			   ">>> [%d] %s, %d bytes -> [default] decrypt and dump\n%s",
-			   seq, qq_get_cmd_desc(cmd), buf_len, hex_dump);
-		g_free(hex_dump);
-		try_dump_as_gbk(data, len);
-	} else {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Fail decrypt packet with default process\n");
-	}
-}
-
-/* process the incoming packet from qq_pending */
-static void _qq_packet_process(guint8 *buf, gint buf_len, PurpleConnection *gc)
-{
-	qq_data *qd;
-	gint len, bytes_expected, bytes_read;
-	guint16 buf_len_read;	/* two bytes in the begining of TCP packet */
-	guint8 *cursor;
-	qq_recv_msg_header header;
-	packet_before_login *b4_packet;
-
-	g_return_if_fail(buf != NULL && buf_len > 0);
-
-	qd = (qq_data *) gc->proto_data;
-	bytes_expected = qd->use_tcp ? QQ_TCP_HEADER_LENGTH : QQ_UDP_HEADER_LENGTH;
-
-	if (buf_len < bytes_expected) {
-		gchar *hex_dump = hex_dump_to_str(buf, buf_len);
-		purple_debug(PURPLE_DEBUG_ERROR,
-			   "QQ", "Received packet is too short, dump and drop\n%s", hex_dump);
-		g_free(hex_dump);
-		return;
-	}
-	/* initialize */
-	cursor = buf;
-	bytes_read = 0;
-
-	/* QQ TCP packet returns first 2 bytes the length of this packet */
-	if (qd->use_tcp) {
-		bytes_read += read_packet_w(buf, &cursor, buf_len, &buf_len_read);
-		if (buf_len_read != buf_len) {	/* wrong */
-			purple_debug
-			    (PURPLE_DEBUG_ERROR,
-			     "QQ",
-			     "TCP read %d bytes, header says %d bytes, use header anyway\n", buf_len, buf_len_read);
-			buf_len = buf_len_read;	/* we believe header is more accurate */
-		}
-	}
-
-	/* now goes the normal QQ packet as UDP packet */
-	bytes_read += read_packet_b(buf, &cursor, buf_len, &header.header_tag);
-	bytes_read += read_packet_w(buf, &cursor, buf_len, &header.source_tag);
-	bytes_read += read_packet_w(buf, &cursor, buf_len, &header.cmd);
-	bytes_read += read_packet_w(buf, &cursor, buf_len, &header.seq);
-
-	if (bytes_read != bytes_expected) {	/* read error */
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
-			   "Fail reading packet header, expect %d bytes, read %d bytes\n", 
-			   bytes_expected, bytes_read);
-		return;
-	}
-
-	if ((buf[buf_len - 1] != QQ_PACKET_TAIL) || (header.header_tag != QQ_PACKET_TAG)) {
-		gchar *hex_dump = hex_dump_to_str(buf, buf_len);
-		purple_debug(PURPLE_DEBUG_ERROR,
-			   "QQ", "Unknown QQ proctocol, dump and drop\n%s", hex_dump);
-		g_free(hex_dump);
-		return;
-	}
-
-	if (QQ_DEBUG)
-		purple_debug(PURPLE_DEBUG_INFO, "QQ",
-			   "==> [%05d] %s, from (%s)\n",
-			   header.seq, qq_get_cmd_desc(header.cmd), qq_get_source_str(header.source_tag));
-
-	if (header.cmd != QQ_CMD_LOGIN && header.cmd != QQ_CMD_REQUEST_LOGIN_TOKEN) {
-		if (!qd->logged_in) {	/* packets before login */
-			b4_packet = g_new0(packet_before_login, 1);
-			/* must duplicate, buffer will be freed after exiting this function */
-			b4_packet->buf = g_memdup(buf, buf_len);
-			b4_packet->len = buf_len;
-			if (qd->before_login_packets == NULL)
-				qd->before_login_packets = g_queue_new();
-			g_queue_push_head(qd->before_login_packets, b4_packet);
-			return;	/* do not process it now */
-		} else if (!g_queue_is_empty(qd->before_login_packets)) {
-			/* logged_in, but we have packets before login */
-			b4_packet = (packet_before_login *)
-			    g_queue_pop_head(qd->before_login_packets);
-			_qq_packet_process(b4_packet->buf, b4_packet->len, gc);
-			/* in fact this is a recursive call,  
-			 * all packets before login will be processed before goes on */
-			g_free(b4_packet->buf);	/* the buf is duplicated, need to be freed */
-			g_free(b4_packet);
-		}
-	}
-
-	/* this is the length of all the encrypted data (also remove tail tag */
-	len = buf_len - (bytes_read) - 1;
-
-	/* whether it is an ack */
-	switch (header.cmd) {
-	case QQ_CMD_RECV_IM:
-	case QQ_CMD_RECV_MSG_SYS:
-	case QQ_CMD_RECV_MSG_FRIEND_CHANGE_STATUS:
-		/* server intiated packet, we need to send ack and check duplicaion 
-		 * this must be put after processing b4_packet
-		 * as these packets will be passed in twice */
-		if (_qq_check_packet_set_window(header.seq, gc)) {
-			purple_debug(PURPLE_DEBUG_WARNING,
-				   "QQ", "dup [%05d] %s, discard...\n", header.seq, qq_get_cmd_desc(header.cmd));
-			return;
-		}
-		break;
-	default:{	/* ack packet, we need to update sendqueue */
-			/* we do not check duplication for server ack */
-			qq_sendqueue_remove(qd, header.seq);
-			if (QQ_DEBUG)
-				purple_debug(PURPLE_DEBUG_INFO, "QQ",
-					   "ack [%05d] %s, remove from sendqueue\n",
-					   header.seq, qq_get_cmd_desc(header.cmd));
-		}
-	}
-
-	/* now process the packet */
-	switch (header.cmd) {
-	case QQ_CMD_KEEP_ALIVE:
-		qq_process_keep_alive_reply(cursor, len, gc);
-		break;
-	case QQ_CMD_UPDATE_INFO:
-		qq_process_modify_info_reply(cursor, len, gc);
-		break;
-	case QQ_CMD_ADD_FRIEND_WO_AUTH:
-		qq_process_add_buddy_reply(cursor, len, header.seq, gc);
-		break;
-	case QQ_CMD_DEL_FRIEND:
-		qq_process_remove_buddy_reply(cursor, len, gc);
-		break;
-	case QQ_CMD_REMOVE_SELF:
-		qq_process_remove_self_reply(cursor, len, gc);
-		break;
-	case QQ_CMD_BUDDY_AUTH:
-		qq_process_add_buddy_auth_reply(cursor, len, gc);
-		break;
-	case QQ_CMD_GET_USER_INFO:
-		qq_process_get_info_reply(cursor, len, gc);
-		break;
-	case QQ_CMD_CHANGE_ONLINE_STATUS:
-		qq_process_change_status_reply(cursor, len, gc);
-		break;
-	case QQ_CMD_SEND_IM:
-		qq_process_send_im_reply(cursor, len, gc);
-		break;
-	case QQ_CMD_RECV_IM:
-		qq_process_recv_im(cursor, len, header.seq, gc);
-		break;
-	case QQ_CMD_LOGIN:
-		qq_process_login_reply(cursor, len, gc);
-		break;
-	case QQ_CMD_GET_FRIENDS_LIST:
-		qq_process_get_buddies_list_reply(cursor, len, gc);
-		break;
-	case QQ_CMD_GET_FRIENDS_ONLINE:
-		qq_process_get_buddies_online_reply(cursor, len, gc);
-		break;
-	case QQ_CMD_GROUP_CMD:
-		qq_process_group_cmd_reply(cursor, len, header.seq, gc);
-		break;
-	case QQ_CMD_GET_ALL_LIST_WITH_GROUP:
-		qq_process_get_all_list_with_group_reply(cursor, len, gc);
-		break;
-	case QQ_CMD_GET_LEVEL:
-		qq_process_get_level_reply(cursor, len, gc);
-		break;
-	case QQ_CMD_REQUEST_LOGIN_TOKEN:
-		qq_process_request_login_token_reply(cursor, len, gc);
-		break;
-	case QQ_CMD_RECV_MSG_SYS:
-		qq_process_msg_sys(cursor, len, header.seq, gc);
-		break;
-	case QQ_CMD_RECV_MSG_FRIEND_CHANGE_STATUS:
-		qq_process_friend_change_status(cursor, len, gc);
-		break;
-	default:
-		_qq_process_packet_default(cursor, len, header.cmd, header.seq, gc);
-		break;
-	}
-}
-
-/* clean up the packets before login */
-void qq_b4_packets_free(qq_data *qd)
-{
-	packet_before_login *b4_packet;
-	g_return_if_fail(qd != NULL);
-	/* now clean up my own data structures */
-	if (qd->before_login_packets != NULL) {
-		while (NULL != (b4_packet = g_queue_pop_tail(qd->before_login_packets))) {
-			g_free(b4_packet->buf);
-			g_free(b4_packet);
-		}
-		g_queue_free(qd->before_login_packets);
-	}
-}
-
-void qq_input_pending(gpointer data, gint source, PurpleInputCondition cond)
-{
-	PurpleConnection *gc;
-	qq_data *qd;
-	guint8 *buf;
-	gint len;
-
-	gc = (PurpleConnection *) data;
-
-	if(cond != PURPLE_INPUT_READ) {
-		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
-			_("Socket error"));
-		return;
-	}
-
-	qd = (qq_data *) gc->proto_data;
-	buf = g_newa(guint8, MAX_PACKET_SIZE);
-
-	/* here we have UDP proxy suppport */
-	len = qq_proxy_read(qd, buf, MAX_PACKET_SIZE);
-	if (len <= 0) {
-		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
-			_("Unable to read from socket"));
-		return;
-	} else {
-		_qq_packet_process(buf, len, gc);
-	}
-}
--- a/libpurple/protocols/qq/recv_core.h	Wed Jul 09 00:27:44 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,36 +0,0 @@
-/**
- * @file recv_core.h
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-
-#ifndef _QQ_RECV_CORE_H_
-#define _QQ_RECV_CORE_H_
-
-#include <glib.h>
-#include "connection.h"
-#include "qq.h"
-
-void qq_b4_packets_free(qq_data *qd);
-
-void qq_input_pending(gpointer data, gint source, PurpleInputCondition cond);
-
-#endif
--- a/libpurple/protocols/qq/send_core.c	Wed Jul 09 00:27:44 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,163 +0,0 @@
-/**
- * @file send_core.c
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-
-#include "debug.h"
-#include "internal.h"
-
-#include "crypt.h"
-#include "header_info.h"
-#include "packet_parse.h"
-#include "qq.h"
-#include "qq_proxy.h"
-#include "send_core.h"
-#include "sendqueue.h"
-
-/* create qq packet header with given sequence
- * return the number of bytes in header if succeeds
- * return -1 if there is any error */
-gint _create_packet_head_seq(guint8 *buf, guint8 **cursor,
-			     PurpleConnection *gc, guint16 cmd, gboolean is_auto_seq, guint16 *seq)
-{
-	qq_data *qd;
-	gint bytes_expected, bytes_written;
-
-	g_return_val_if_fail(buf != NULL && cursor != NULL && *cursor != NULL, -1);
-
-	qd = (qq_data *) gc->proto_data;
-	if (is_auto_seq)
-		*seq = ++(qd->send_seq);
-
-	*cursor = buf;
-	bytes_written = 0;
-	bytes_expected = (qd->use_tcp) ? QQ_TCP_HEADER_LENGTH : QQ_UDP_HEADER_LENGTH;
-
-	/* QQ TCP packet has two bytes in the begining defines packet length
-	 * so I leave room here for size */
-	if (qd->use_tcp)
-		bytes_written += create_packet_w(buf, cursor, 0x0000);
-
-	/* now comes the normal QQ packet as UDP */
-	bytes_written += create_packet_b(buf, cursor, QQ_PACKET_TAG);
-	bytes_written += create_packet_w(buf, cursor, QQ_CLIENT);
-	bytes_written += create_packet_w(buf, cursor, cmd);
-	bytes_written += create_packet_w(buf, cursor, *seq);
-
-	if (bytes_written != bytes_expected) {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
-			   "Fail create qq header, expect %d bytes, written %d bytes\n", bytes_expected, bytes_written);
-		bytes_written = -1;
-	}
-	return bytes_written;
-}
-
-/* for those need ack and resend no ack feed back from server
- * return number of bytes written to the socket,
- * return -1 if there is any error */
-gint _qq_send_packet(PurpleConnection *gc, guint8 *buf, gint len, guint16 cmd)
-{
-	qq_data *qd;
-	qq_sendpacket *p;
-	gint bytes_sent;
-	guint8 *cursor;
-
-	qd = (qq_data *) gc->proto_data;
-
-	if (qd->use_tcp) {
-		if (len > MAX_PACKET_SIZE) {
-			purple_debug(PURPLE_DEBUG_ERROR, "QQ",
-				   "xxx [%05d] %s, %d bytes is too large, do not send\n",
-				   qd->send_seq, qq_get_cmd_desc(cmd), len);
-			return -1;
-		} else {	/* I update the len for TCP packet */
-			cursor = buf;
-			create_packet_w(buf, &cursor, len);
-		}
-	}
-
-	bytes_sent = qq_proxy_write(qd, buf, len);
-
-	if (bytes_sent >= 0) {		/* put to queue, for matching server ACK usage */
-		p = g_new0(qq_sendpacket, 1);
-		p->fd = qd->fd;
-		p->cmd = cmd;
-		p->send_seq = qd->send_seq;
-		p->resend_times = 0;
-		p->sendtime = time(NULL);
-		p->buf = g_memdup(buf, len);	/* don't use g_strdup, may have 0x00 */
-		p->len = len;
-		qd->sendqueue = g_list_append(qd->sendqueue, p);
-	}
-
-	return bytes_sent;
-}
-
-/* send the packet generated with the given cmd and data
- * return the number of bytes sent to socket if succeeds
- * return -1 if there is any error */
-gint qq_send_cmd(PurpleConnection *gc, guint16 cmd,
-		 gboolean is_auto_seq, guint16 seq, gboolean need_ack, guint8 *data, gint len)
-{
-	qq_data *qd;
-	guint8 *buf, *cursor, *encrypted_data;
-	guint16 seq_ret;
-	gint encrypted_len, bytes_written, bytes_expected, bytes_sent;
-
-	qd = (qq_data *) gc->proto_data;
-	g_return_val_if_fail(qd->session_key != NULL, -1);
-
-	buf = g_newa(guint8, MAX_PACKET_SIZE);
-	encrypted_len = len + 16;	/* at most 16 bytes more */
-	encrypted_data = g_newa(guint8, encrypted_len);
-	cursor = buf;
-	bytes_written = 0;
-
-	qq_encrypt(data, len, qd->session_key, encrypted_data, &encrypted_len);
-
-	seq_ret = seq;
-	if (_create_packet_head_seq(buf, &cursor, gc, cmd, is_auto_seq, &seq_ret) >= 0) {
-		bytes_expected = 4 + encrypted_len + 1;
-		bytes_written += create_packet_dw(buf, &cursor, (guint32) qd->uid);
-		bytes_written += create_packet_data(buf, &cursor, encrypted_data, encrypted_len);
-		bytes_written += create_packet_b(buf, &cursor, QQ_PACKET_TAIL);
-		if (bytes_written == bytes_expected) {	/* packet OK */
-			/* if it does not need ACK, we send ACK manually several times */
-			if (need_ack)   /* my request, send it */
-				bytes_sent = _qq_send_packet(gc, buf, cursor - buf, cmd);
-			else		/* server's request, send ACK */
-				bytes_sent = qq_proxy_write(qd, buf, cursor - buf);
-
-			if (QQ_DEBUG)
-				purple_debug(PURPLE_DEBUG_INFO, "QQ",
-					   "<== [%05d] %s, %d bytes\n", seq_ret, qq_get_cmd_desc(cmd), bytes_sent);
-			return bytes_sent;
-		} else {	/* bad packet */
-			purple_debug(PURPLE_DEBUG_ERROR, "QQ",
-				   "Fail creating packet, expect %d bytes, written %d bytes\n",
-				   bytes_expected, bytes_written);
-			return -1;
-		}
-	}
-
-	return -1;
-}
--- a/libpurple/protocols/qq/send_core.h	Wed Jul 09 00:27:44 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,37 +0,0 @@
-/**
- * @file send_core.h
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-
-#ifndef _QQ_SEND_CORE_H_
-#define _QQ_SEND_CORE_H_
-
-#include <glib.h>
-#include "connection.h"
-
-gint qq_send_cmd(PurpleConnection *gc, guint16 cmd, gboolean is_auto_seq, guint16 seq, 
-		gboolean need_ack, guint8 *data, gint len);
-gint _qq_send_packet(PurpleConnection * gc, guint8 *buf, gint len, guint16 cmd);
-gint _create_packet_head_seq(guint8 *buf, guint8 **cursor,
-		PurpleConnection *gc, guint16 cmd, gboolean is_auto_seq, guint16 *seq);
-
-#endif
--- a/libpurple/protocols/qq/send_file.c	Wed Jul 09 00:27:44 2008 +0000
+++ b/libpurple/protocols/qq/send_file.c	Wed Jul 09 00:32:18 2008 +0000
@@ -36,7 +36,7 @@
 #include "im.h"
 #include "keep_alive.h"
 #include "packet_parse.h"
-#include "send_core.h"
+#include "qq_network.h"
 #include "utils.h"
 
 enum
@@ -103,6 +103,7 @@
 	return send(info->sender_fd, buf, len, 0);
 }
 */
+
 static ssize_t _qq_xfer_udp_send(const guint8 *buf, size_t len, PurpleXfer *xfer)
 {
 	struct sockaddr_in sin;
@@ -243,42 +244,45 @@
 	g_free(internet_ip_str);
 }
 
-void qq_get_conn_info(guint8 *data, guint8 **cursor, gint data_len, ft_info *info)
+#define QQ_CONN_INFO_LEN	61
+gint qq_get_conn_info(ft_info *info, guint8 *data)
 {
-	read_packet_data(data, cursor, data_len, info->file_session_key, 16);
-	*cursor += 30;
-	read_packet_b(data, cursor, data_len, &info->conn_method);
-	read_packet_dw(data, cursor, data_len, &info->remote_internet_ip);
-	read_packet_w(data, cursor, data_len, &info->remote_internet_port);
-	read_packet_w(data, cursor, data_len, &info->remote_major_port);
-	read_packet_dw(data, cursor, data_len, &info->remote_real_ip);
-	read_packet_w(data, cursor, data_len, &info->remote_minor_port);
+	gint bytes = 0;
+	/* 16 + 30 + 1 + 4 + 2 + 2 + 4 + 2 = 61 */
+	bytes += qq_getdata(info->file_session_key, 16, data + bytes);
+	bytes += 30;	/* skip 30 bytes */
+	bytes += qq_get8(&info->conn_method, data + bytes);
+	bytes += qq_get32(&info->remote_internet_ip, data + bytes);
+	bytes += qq_get16(&info->remote_internet_port, data + bytes);
+	bytes += qq_get16(&info->remote_major_port, data + bytes);
+	bytes += qq_get32(&info->remote_real_ip, data + bytes);
+	bytes += qq_get16(&info->remote_minor_port, data + bytes);
 	qq_show_conn_info(info);
+	return bytes;
 }
 
-gint qq_fill_conn_info(guint8 *raw_data, guint8 **cursor, ft_info *info)
+gint qq_fill_conn_info(guint8 *raw_data, ft_info *info)
 {
-	gint bytes;
-	bytes = 0;
+	gint bytes = 0;
 	/* 064: connection method, UDP 0x00, TCP 0x03 */
-	bytes += create_packet_b (raw_data, cursor, info->conn_method);
+	bytes += qq_put8 (raw_data + bytes, info->conn_method);
 	/* 065-068: outer ip address of sender (proxy address) */
-	bytes += create_packet_dw (raw_data, cursor, info->local_internet_ip);
+	bytes += qq_put32 (raw_data + bytes, info->local_internet_ip);
 	/* 069-070: sender port */
-	bytes += create_packet_w (raw_data, cursor, info->local_internet_port);
+	bytes += qq_put16 (raw_data + bytes, info->local_internet_port);
 	/* 071-072: the first listening port(TCP doesn't have this part) */
-	bytes += create_packet_w (raw_data, cursor, info->local_major_port);
+	bytes += qq_put16 (raw_data + bytes, info->local_major_port);
 	/* 073-076: real ip */
-	bytes += create_packet_dw (raw_data, cursor, info->local_real_ip);
+	bytes += qq_put32 (raw_data + bytes, info->local_real_ip);
 	/* 077-078: the second listening port */
-	bytes += create_packet_w (raw_data, cursor, info->local_minor_port);
+	bytes += qq_put16 (raw_data + bytes, info->local_minor_port);
 	return bytes;
 }
 
 
 /* fill in the common information of file transfer */
 static gint _qq_create_packet_file_header
-(guint8 *raw_data, guint8 **cursor, guint32 to_uid, guint16 message_type, qq_data *qd, gboolean seq_ack)
+(guint8 *raw_data, guint32 to_uid, guint16 message_type, qq_data *qd, gboolean seq_ack)
 {
 	gint bytes;
 	time_t now;
@@ -294,42 +298,42 @@
 	}
 
 	/* 000-003: receiver uid */
-	bytes += create_packet_dw (raw_data, cursor, qd->uid);
+	bytes += qq_put32 (raw_data + bytes, qd->uid);
 	/* 004-007: sender uid */
-	bytes += create_packet_dw (raw_data, cursor, to_uid);
+	bytes += qq_put32 (raw_data + bytes, to_uid);
 	/* 008-009: sender client version */
-	bytes += create_packet_w (raw_data, cursor, QQ_CLIENT);
+	bytes += qq_put16 (raw_data + bytes, QQ_CLIENT);
 	/* 010-013: receiver uid */
-	bytes += create_packet_dw (raw_data, cursor, qd->uid);
+	bytes += qq_put32 (raw_data + bytes, qd->uid);
 	/* 014-017: sender uid */
-	bytes += create_packet_dw (raw_data, cursor, to_uid);
+	bytes += qq_put32 (raw_data + bytes, to_uid);
 	/* 018-033: md5 of (uid+session_key) */
-	bytes += create_packet_data (raw_data, cursor, qd->session_md5, 16);
+	bytes += qq_putdata (raw_data + bytes, qd->session_md5, 16);
 	/* 034-035: message type */
-	bytes += create_packet_w (raw_data, cursor, message_type);
+	bytes += qq_put16 (raw_data + bytes, message_type);
 	/* 036-037: sequence number */
-	bytes += create_packet_w (raw_data, cursor, seq);
+	bytes += qq_put16 (raw_data + bytes, seq);
 	/* 038-041: send time */
-	bytes += create_packet_dw (raw_data, cursor, (guint32) now);
+	bytes += qq_put32 (raw_data + bytes, (guint32) now);
 	/* 042-042: always 0x00 */
-	bytes += create_packet_b (raw_data, cursor, 0x00);
+	bytes += qq_put8 (raw_data + bytes, 0x00);
 	/* 043-043: sender icon */
-	bytes += create_packet_b (raw_data, cursor, qd->my_icon);
+	bytes += qq_put8 (raw_data + bytes, qd->my_icon);
 	/* 044-046: always 0x00 */
-	bytes += create_packet_w (raw_data, cursor, 0x0000);
-	bytes += create_packet_b (raw_data, cursor, 0x00);
+	bytes += qq_put16 (raw_data + bytes, 0x0000);
+	bytes += qq_put8 (raw_data + bytes, 0x00);
 	/* 047-047: we use font attr */
-	bytes += create_packet_b (raw_data, cursor, 0x01);
+	bytes += qq_put8 (raw_data + bytes, 0x01);
 	/* 048-051: always 0x00 */
-	bytes += create_packet_dw (raw_data, cursor, 0x00000000);
+	bytes += qq_put32 (raw_data + bytes, 0x00000000);
 
 	/* 052-062: always 0x00 */
-	bytes += create_packet_dw (raw_data, cursor, 0x00000000);
-	bytes += create_packet_dw (raw_data, cursor, 0x00000000);
-	bytes += create_packet_w (raw_data, cursor, 0x0000);
-	bytes += create_packet_b (raw_data, cursor, 0x00);
+	bytes += qq_put32 (raw_data + bytes, 0x00000000);
+	bytes += qq_put32 (raw_data + bytes, 0x00000000);
+	bytes += qq_put16 (raw_data + bytes, 0x0000);
+	bytes += qq_put8 (raw_data + bytes, 0x00);
 	/* 063: transfer_type,  0x65: FILE 0x6b: FACE */
-	bytes += create_packet_b (raw_data, cursor, QQ_FILE_TRANSFER_FILE); /* FIXME */
+	bytes += qq_put8 (raw_data + bytes, QQ_FILE_TRANSFER_FILE); /* FIXME */
 
 	return bytes;
 }
@@ -433,7 +437,7 @@
 static void _qq_send_packet_file_request (PurpleConnection *gc, guint32 to_uid, gchar *filename, gint filesize)
 {
 	qq_data *qd;
-	guint8 *cursor, *raw_data;
+	guint8 *raw_data;
 	gchar *filelen_str;
 	gint filename_len, filelen_strlen, packet_len, bytes;
 	ft_info *info;
@@ -455,27 +459,24 @@
 
 	packet_len = 82 + filename_len + filelen_strlen;
 	raw_data = g_newa(guint8, packet_len);
-	cursor = raw_data;
+	bytes = 0;
 
-	bytes = _qq_create_packet_file_header(raw_data, &cursor, to_uid, 
+	bytes += _qq_create_packet_file_header(raw_data + bytes, to_uid, 
 			QQ_FILE_TRANS_REQ, qd, FALSE);
-	bytes += qq_fill_conn_info(raw_data, &cursor, info);
+	bytes += qq_fill_conn_info(raw_data + bytes, info);
 	/* 079: 0x20 */
-	bytes += create_packet_b (raw_data, &cursor, 0x20);
+	bytes += qq_put8 (raw_data + bytes, 0x20);
 	/* 080: 0x1f */
-	bytes += create_packet_b (raw_data, &cursor, 0x1f);
+	bytes += qq_put8 (raw_data + bytes, 0x1f);
 	/* undetermined len: filename */
-	bytes += create_packet_data (raw_data, &cursor, (guint8 *) filename,
-				     filename_len);
+	bytes += qq_putdata (raw_data + bytes, (guint8 *) filename, filename_len);
 	/* 0x1f */
-	bytes += create_packet_b (raw_data, &cursor, 0x1f);
+	bytes += qq_put8 (raw_data + bytes, 0x1f);
 	/* file length */
-	bytes += create_packet_data (raw_data, &cursor, (guint8 *) filelen_str,
-				     filelen_strlen);
+	bytes += qq_putdata (raw_data + bytes, (guint8 *) filelen_str, filelen_strlen);
 
 	if (packet_len == bytes)
-		qq_send_cmd (gc, QQ_CMD_SEND_IM, TRUE, 0, TRUE, raw_data,
-			     cursor - raw_data);
+		qq_send_cmd (qd, QQ_CMD_SEND_IM, raw_data, bytes);
 	else
 		purple_debug (PURPLE_DEBUG_INFO, "qq_send_packet_file_request",
 			    "%d bytes expected but got %d bytes\n",
@@ -488,7 +489,7 @@
 static void _qq_send_packet_file_accept(PurpleConnection *gc, guint32 to_uid)
 {
 	qq_data *qd;
-	guint8 *cursor, *raw_data;
+	guint8 *raw_data;
 	guint16 minor_port;
 	guint32 real_ip;
 	gint packet_len, bytes;
@@ -502,22 +503,21 @@
 
 	packet_len = 79;
 	raw_data = g_newa (guint8, packet_len);
-	cursor = raw_data;
+	bytes = 0;
 
 	minor_port = info->local_minor_port;
 	real_ip = info->local_real_ip;
 	info->local_minor_port = 0;
 	info->local_real_ip = 0;
 
-	bytes = _qq_create_packet_file_header(raw_data, &cursor, to_uid, QQ_FILE_TRANS_ACC_UDP, qd, TRUE);
-	bytes += qq_fill_conn_info(raw_data, &cursor, info);
+	bytes += _qq_create_packet_file_header(raw_data + bytes, to_uid, QQ_FILE_TRANS_ACC_UDP, qd, TRUE);
+	bytes += qq_fill_conn_info(raw_data + bytes, info);
 
 	info->local_minor_port = minor_port;
 	info->local_real_ip = real_ip;
 
 	if (packet_len == bytes)
-		qq_send_cmd (gc, QQ_CMD_SEND_IM, TRUE, 0, TRUE, raw_data,
-			     cursor - raw_data);
+		qq_send_cmd (qd, QQ_CMD_SEND_IM, raw_data, bytes);
 	else
 		purple_debug (PURPLE_DEBUG_INFO, "qq_send_packet_file_accept",
 			    "%d bytes expected but got %d bytes\n",
@@ -529,7 +529,7 @@
 	PurpleXfer *xfer;
 	ft_info *info;
 	qq_data *qd;
-	guint8 *cursor, *raw_data;
+	guint8 *raw_data;
 	gint packet_len, bytes;
 
 	qd = (qq_data *) gc->proto_data;
@@ -538,14 +538,13 @@
 
 	packet_len = 79;
 	raw_data = g_newa (guint8, packet_len);
-	cursor = raw_data;
+	bytes = 0;
 
 	purple_debug(PURPLE_DEBUG_INFO, "QQ", "<== sending qq file notify ip packet\n");
-	bytes = _qq_create_packet_file_header(raw_data, &cursor, to_uid, QQ_FILE_TRANS_NOTIFY, qd, TRUE);
-	bytes += qq_fill_conn_info(raw_data, &cursor, info);
+	bytes += _qq_create_packet_file_header(raw_data + bytes, to_uid, QQ_FILE_TRANS_NOTIFY, qd, TRUE);
+	bytes += qq_fill_conn_info(raw_data + bytes, info);
 	if (packet_len == bytes)
-		qq_send_cmd (gc, QQ_CMD_SEND_IM, TRUE, 0, TRUE, raw_data,
-			     cursor - raw_data);
+		qq_send_cmd (qd, QQ_CMD_SEND_IM, raw_data, bytes);
 	else
 		purple_debug (PURPLE_DEBUG_INFO, "qq_send_packet_file_notify",
 			    "%d bytes expected but got %d bytes\n",
@@ -560,7 +559,7 @@
 static void _qq_send_packet_file_reject (PurpleConnection *gc, guint32 to_uid)
 {
 	qq_data *qd;
-	guint8 *cursor, *raw_data;
+	guint8 *raw_data;
 	gint packet_len, bytes;
 
 	purple_debug(PURPLE_DEBUG_INFO, "_qq_send_packet_file_reject", "start");
@@ -568,14 +567,12 @@
 
 	packet_len = 64;
 	raw_data = g_newa (guint8, packet_len);
-	cursor = raw_data;
 	bytes = 0;
 
-	bytes = _qq_create_packet_file_header(raw_data, &cursor, to_uid, QQ_FILE_TRANS_DENY_UDP, qd, TRUE);
+	bytes += _qq_create_packet_file_header(raw_data + bytes, to_uid, QQ_FILE_TRANS_DENY_UDP, qd, TRUE);
 
 	if (packet_len == bytes)
-		qq_send_cmd (gc, QQ_CMD_SEND_IM, TRUE, 0, TRUE, raw_data,
-			     cursor - raw_data);
+		qq_send_cmd (qd, QQ_CMD_SEND_IM, raw_data, bytes);
 	else
 		purple_debug (PURPLE_DEBUG_INFO, "qq_send_packet_file",
 			    "%d bytes expected but got %d bytes\n",
@@ -586,7 +583,7 @@
 static void _qq_send_packet_file_cancel (PurpleConnection *gc, guint32 to_uid)
 {
 	qq_data *qd;
-	guint8 *cursor, *raw_data;
+	guint8 *raw_data;
 	gint packet_len, bytes;
 
 	purple_debug(PURPLE_DEBUG_INFO, "_qq_send_packet_file_cancel", "start\n");
@@ -594,17 +591,15 @@
 
 	packet_len = 64;
 	raw_data = g_newa (guint8, packet_len);
-	cursor = raw_data;
 	bytes = 0;
 
 	purple_debug(PURPLE_DEBUG_INFO, "_qq_send_packet_file_cancel", "before create header\n");
-	bytes = _qq_create_packet_file_header(raw_data, &cursor, to_uid, QQ_FILE_TRANS_CANCEL, qd, TRUE);
+	bytes += _qq_create_packet_file_header(raw_data + bytes, to_uid, QQ_FILE_TRANS_CANCEL, qd, TRUE);
 	purple_debug(PURPLE_DEBUG_INFO, "_qq_send_packet_file_cancel", "end create header\n");
 
 	if (packet_len == bytes) {
 		purple_debug(PURPLE_DEBUG_INFO, "_qq_send_packet_file_cancel", "before send cmd\n");
-		qq_send_cmd (gc, QQ_CMD_SEND_IM, TRUE, 0, TRUE, raw_data,
-			     cursor - raw_data);
+		qq_send_cmd (qd, QQ_CMD_SEND_IM, raw_data, bytes);
 	}
 	else
 		purple_debug (PURPLE_DEBUG_INFO, "qq_send_packet_file",
@@ -688,7 +683,7 @@
 }
 
 /* process reject im for file transfer request */
-void qq_process_recv_file_reject (guint8 *data, guint8 **cursor, gint data_len, 
+void qq_process_recv_file_reject (guint8 *data, gint data_len, 
 		guint32 sender_uid, PurpleConnection *gc)
 {
 	gchar *msg, *filename;
@@ -698,11 +693,13 @@
 	qd = (qq_data *) gc->proto_data;
 	g_return_if_fail (qd->xfer != NULL);
 
+	/*	border has been checked before
 	if (*cursor >= (data + data_len - 1)) {
 		purple_debug (PURPLE_DEBUG_WARNING, "QQ",
 			    "Received file reject message is empty\n");
 		return;
 	}
+	*/
 	filename = strrchr(purple_xfer_get_local_filename(qd->xfer), '/') + 1;
 	msg = g_strdup_printf(_("%d has declined the file %s"),
 		 sender_uid, filename);
@@ -715,7 +712,7 @@
 }
 
 /* process cancel im for file transfer request */
-void qq_process_recv_file_cancel (guint8 *data, guint8 **cursor, gint data_len, 
+void qq_process_recv_file_cancel (guint8 *data, gint data_len, 
 		guint32 sender_uid, PurpleConnection *gc)
 {
 	gchar *msg, *filename;
@@ -726,11 +723,13 @@
 	g_return_if_fail (qd->xfer != NULL
 			&& purple_xfer_get_filename(qd->xfer) != NULL);
 
+	/*	border has been checked before
 	if (*cursor >= (data + data_len - 1)) {
 		purple_debug (PURPLE_DEBUG_WARNING, "QQ",
 			    "Received file reject message is empty\n");
 		return;
 	}
+	*/
 	filename = strrchr(purple_xfer_get_local_filename(qd->xfer), '/') + 1;
 	msg = g_strdup_printf
 		(_("%d canceled the transfer of %s"),
@@ -744,27 +743,26 @@
 }
 
 /* process accept im for file transfer request */
-void qq_process_recv_file_accept(guint8 *data, guint8 **cursor, gint data_len, 
-		guint32 sender_uid, PurpleConnection *gc)
+void qq_process_recv_file_accept(guint8 *data, gint data_len, guint32 sender_uid, PurpleConnection *gc)
 {
 	qq_data *qd;
+	gint bytes;
 	ft_info *info;
 	PurpleXfer *xfer;
 
 	g_return_if_fail (data != NULL && data_len != 0);
 	qd = (qq_data *) gc->proto_data;
 	xfer = qd->xfer;
+	info = (ft_info *) qd->xfer->data;
 
-	if (*cursor >= (data + data_len - 1)) {
+	if (data_len <= 30 + QQ_CONN_INFO_LEN) {
 		purple_debug (PURPLE_DEBUG_WARNING, "QQ",
 			    "Received file reject message is empty\n");
 		return;
 	}
 
-	info = (ft_info *) qd->xfer->data;
-
-	*cursor = data + 18 + 12;
-	qq_get_conn_info(data, cursor, data_len, info);
+	bytes = 18 + 12;	/* skip 30 bytes */
+	qq_get_conn_info(info, data + bytes);
 	_qq_xfer_init_socket(qd->xfer);
 
 	_qq_xfer_init_udp_channel(info);
@@ -772,8 +770,7 @@
 }
 
 /* process request from buddy's im for file transfer request */
-void qq_process_recv_file_request(guint8 *data, guint8 **cursor, gint data_len, 
-		guint32 sender_uid, PurpleConnection * gc)
+void qq_process_recv_file_request(guint8 *data, gint data_len, guint32 sender_uid, PurpleConnection * gc)
 {
 	qq_data *qd;
 	PurpleXfer *xfer;
@@ -781,25 +778,27 @@
 	ft_info *info;
 	PurpleBuddy *b;
 	qq_buddy *q_bud;
+	gint bytes;
 
 	g_return_if_fail (data != NULL && data_len != 0);
 	qd = (qq_data *) gc->proto_data;
 
-	if (*cursor >= (data + data_len - 1)) {
-		purple_debug (PURPLE_DEBUG_WARNING, "QQ",
-			    "Received file reject message is empty\n");
-		return;
-	}
-
-	info = g_new0(ft_info, 1);
+	info = g_newa(ft_info, 1);
 	info->local_internet_ip = g_ntohl(inet_addr(qd->my_ip));
 	info->local_internet_port = qd->my_port;
 	info->local_real_ip = 0x00000000;
 	info->to_uid = sender_uid;
-	read_packet_w(data, cursor, data_len, &(info->send_seq));
+	
+	if (data_len <= 2 + 30 + QQ_CONN_INFO_LEN) {
+		purple_debug (PURPLE_DEBUG_WARNING, "QQ",
+			    "Received file request message is empty\n");
+		return;
+	}
+	bytes = 0;
+	bytes += qq_get16(&(info->send_seq), data + bytes);
 
-	*cursor = data + 18 + 12;
-	qq_get_conn_info(data, cursor, data_len, info);
+	bytes += 18 + 12;	/* skip 30 bytes */
+	bytes += qq_get_conn_info(info, data + bytes);
 
 	fileinfo = g_strsplit((gchar *) (data + 81 + 12), "\x1f", 2);
 	g_return_if_fail (fileinfo != NULL && fileinfo[0] != NULL && fileinfo[1] != NULL);
@@ -880,9 +879,10 @@
 	*/
 }
 
-void qq_process_recv_file_notify(guint8 *data, guint8 **cursor, gint data_len, 
+void qq_process_recv_file_notify(guint8 *data, gint data_len, 
 		guint32 sender_uid, PurpleConnection *gc)
 {
+	gint bytes;
 	qq_data *qd;
 	ft_info *info;
 	PurpleXfer *xfer;
@@ -890,19 +890,19 @@
 	g_return_if_fail (data != NULL && data_len != 0);
 	qd = (qq_data *) gc->proto_data;
 
-	if (*cursor >= (data + data_len - 1)) {
+	xfer = qd->xfer;
+	info = (ft_info *) qd->xfer->data;
+	if (data_len <= 2 + 30 + QQ_CONN_INFO_LEN) {
 		purple_debug (PURPLE_DEBUG_WARNING, "QQ",
 			    "Received file notify message is empty\n");
 		return;
 	}
+	
+	bytes = 0;
+	bytes += qq_get16(&(info->send_seq), data + bytes);
 
-	xfer = qd->xfer;
-	info = (ft_info *) qd->xfer->data;
-	/* FIXME */
-	read_packet_w(data, cursor, data_len, &(info->send_seq));
-
-	*cursor = data + 18 + 12;
-	qq_get_conn_info(data, cursor, data_len, info);
+	bytes += 18 + 12;
+	bytes += qq_get_conn_info(info, data + bytes);
 
 	_qq_xfer_init_udp_channel(info);
 
@@ -938,7 +938,7 @@
 /*
 static void qq_send_packet_request_key(PurpleConnection *gc, guint8 key)
 {
-	qq_send_cmd(gc, QQ_CMD_REQUEST_KEY, TRUE, 0, TRUE, &key, 1);
+	qq_send_cmd(gc, QQ_CMD_REQUEST_KEY, &key, 1);
 }
 
 static void qq_process_recv_request_key(PurpleConnection *gc)
--- a/libpurple/protocols/qq/send_file.h	Wed Jul 09 00:27:44 2008 +0000
+++ b/libpurple/protocols/qq/send_file.h	Wed Jul 09 00:32:18 2008 +0000
@@ -66,20 +66,15 @@
 	gboolean use_major;
 } ft_info;
 
-void qq_process_recv_file_accept(guint8 *data, guint8 **cursor, gint data_len, 
-		guint32 sender_uid, PurpleConnection *gc);
-void qq_process_recv_file_reject(guint8 *data, guint8 **cursor, gint data_len, 
-		guint32 sender_uid, PurpleConnection *gc);
-void qq_process_recv_file_cancel(guint8 *data, guint8 **cursor, gint data_len, 
-		guint32 sender_uid, PurpleConnection *gc);
-void qq_process_recv_file_request(guint8 *data, guint8 **cursor, gint data_len, 
-		guint32 sender_uid, PurpleConnection *gc);
-void qq_process_recv_file_notify(guint8 *data, guint8 **cursor, gint data_len, 
-		guint32 sender_uid, PurpleConnection *gc);
+void qq_process_recv_file_accept(guint8 *data, gint data_len, guint32 sender_uid, PurpleConnection *gc);
+void qq_process_recv_file_reject(guint8 *data, gint data_len, guint32 sender_uid, PurpleConnection *gc);
+void qq_process_recv_file_cancel(guint8 *data, gint data_len, guint32 sender_uid, PurpleConnection *gc);
+void qq_process_recv_file_request(guint8 *data, gint data_len, guint32 sender_uid, PurpleConnection *gc);
+void qq_process_recv_file_notify(guint8 *data, gint data_len, guint32 sender_uid, PurpleConnection *gc);
 gboolean qq_can_receive_file(PurpleConnection *gc, const char *who);
 void qq_send_file(PurpleConnection *gc, const char *who, const char *file);
-void qq_get_conn_info(guint8 *data, guint8 **cursor, gint data_len, ft_info *info);
-gint qq_fill_conn_info(guint8 *data, guint8 **cursor, ft_info *info);
+gint qq_get_conn_info(ft_info *info, guint8 *data);
+gint qq_fill_conn_info(guint8 *data, ft_info *info);
 gssize _qq_xfer_write(const guint8 *buf, size_t len, PurpleXfer *xfer);
 
 #endif
--- a/libpurple/protocols/qq/sendqueue.c	Wed Jul 09 00:27:44 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,156 +0,0 @@
-/**
- * @file sendqueue.c
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-
-#include "internal.h"
-
-#include "connection.h"
-#include "debug.h"
-#include "notify.h"
-#include "prefs.h"
-#include "request.h"
-
-#include "header_info.h"
-#include "qq_proxy.h"
-#include "sendqueue.h"
-
-#define QQ_RESEND_MAX               8	/* max resend per packet */
-
-typedef struct _gc_and_packet gc_and_packet;
-
-struct _gc_and_packet {
-	PurpleConnection *gc;
-	qq_sendpacket *packet;
-};
-
-/* Remove a packet with send_seq from sendqueue */
-void qq_sendqueue_remove(qq_data *qd, guint16 send_seq)
-{
-	GList *list;
-	qq_sendpacket *p;
-
-	list = qd->sendqueue;
-	while (list != NULL) {
-		p = (qq_sendpacket *) (list->data);
-		if (p->send_seq == send_seq) {
-			qd->sendqueue = g_list_remove(qd->sendqueue, p);
-			g_free(p->buf);
-			g_free(p);
-			break;
-		}
-		list = list->next;
-	}
-}
-		
-/* clean up sendqueue and free all contents */
-void qq_sendqueue_free(qq_data *qd)
-{
-	qq_sendpacket *p;
-	gint i;
-
-	i = 0;
-	while (qd->sendqueue != NULL) {
-		p = (qq_sendpacket *) (qd->sendqueue->data);
-		qd->sendqueue = g_list_remove(qd->sendqueue, p);
-		g_free(p->buf);
-		g_free(p);
-		i++;
-	}
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "%d packets in sendqueue are freed!\n", i);
-}
-
-/* FIXME We shouldn't be dropping packets, but for now we have to because
- * somewhere we're generating invalid packets that the server won't ack.
- * Given enough time, a buildup of those packets would crash the client. */
-gboolean qq_sendqueue_timeout_callback(gpointer data)
-{
-	PurpleConnection *gc;
-	qq_data *qd;
-	GList *list;
-	qq_sendpacket *p;
-	time_t now;
-	gint wait_time;
-
-	gc = (PurpleConnection *) data;
-	qd = (qq_data *) gc->proto_data;
-	now = time(NULL);
-	list = qd->sendqueue;
-
-	/* empty queue, return TRUE so that timeout continues functioning */
-	if (qd->sendqueue == NULL)
-		return TRUE;
-
-	while (list != NULL) {	/* remove all packet whose resend_times == -1 */
-		p = (qq_sendpacket *) list->data;
-		if (p->resend_times == -1) {	/* to remove */
-			qd->sendqueue = g_list_remove(qd->sendqueue, p);
-			g_free(p->buf);
-			g_free(p);
-			list = qd->sendqueue;
-		} else {
-			list = list->next;
-		}
-	}
-
-	list = qd->sendqueue;
-	while (list != NULL) {
-		p = (qq_sendpacket *) list->data;
-		if (p->resend_times == QQ_RESEND_MAX) {	/* reach max */
-			switch (p->cmd) {
-			case QQ_CMD_KEEP_ALIVE:
-				if (qd->logged_in) {
-					purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Connection lost!\n");
-					purple_connection_error_reason(gc,
-						PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Connection lost"));
-					qd->logged_in = FALSE;
-				}
-				p->resend_times = -1;
-				break;
-			case QQ_CMD_LOGIN:
-			case QQ_CMD_REQUEST_LOGIN_TOKEN:
-				if (!qd->logged_in)	/* cancel login progress */
-					purple_connection_error_reason(gc,
-						PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Login failed, no reply"));
-				p->resend_times = -1;
-				break;
-			default:{
-				purple_debug(PURPLE_DEBUG_WARNING, "QQ", 
-					"%s packet sent %d times but not acked. Not resending it.\n", 
-					qq_get_cmd_desc(p->cmd), QQ_RESEND_MAX);
-				}
-				p->resend_times = -1;
-			}
-		} else {	/* resend_times < QQ_RESEND_MAX, so sent it again */
-			wait_time = (gint) (QQ_SENDQUEUE_TIMEOUT / 1000);
-			if (difftime(now, p->sendtime) > (wait_time * (p->resend_times + 1))) {
-				qq_proxy_write(qd, p->buf, p->len);
-				p->resend_times++;
-				purple_debug(PURPLE_DEBUG_INFO,
-					   "QQ", "<<< [%05d] send again for %d times!\n", 
-					   p->send_seq, p->resend_times);
-			}
-		}
-		list = list->next;
-	}
-	return TRUE;		/* if we return FALSE, the timeout callback stops functioning */
-}
--- a/libpurple/protocols/qq/sendqueue.h	Wed Jul 09 00:27:44 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,50 +0,0 @@
-/**
- * @file sendqueue.h
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-
-#ifndef _QQ_SEND_QUEUE_H_
-#define _QQ_SEND_QUEUE_H_
-
-#include <glib.h>
-#include "qq.h"
-
-#define QQ_SENDQUEUE_TIMEOUT 			5000	/* in 1/1000 sec */
-
-typedef struct _qq_sendpacket qq_sendpacket;
-
-struct _qq_sendpacket {
-	gint fd;
-	gint len;
-	guint8 *buf;
-	guint16 cmd;
-	guint16 send_seq;
-	gint resend_times;
-	time_t sendtime;
-};
-
-void qq_sendqueue_free(qq_data *qd);
-
-void qq_sendqueue_remove(qq_data *qd, guint16 send_seq);
-gboolean qq_sendqueue_timeout_callback(gpointer data);
-
-#endif
--- a/libpurple/protocols/qq/sys_msg.c	Wed Jul 09 00:27:44 2008 +0000
+++ b/libpurple/protocols/qq/sys_msg.c	Wed Jul 09 00:32:18 2008 +0000
@@ -35,7 +35,7 @@
 #include "header_info.h"
 #include "packet_parse.h"
 #include "qq.h"
-#include "send_core.h"
+#include "qq_network.h"
 #include "sys_msg.h"
 #include "utils.h"
 
@@ -120,27 +120,29 @@
 /* Send ACK if the sys message needs an ACK */
 static void _qq_send_packet_ack_msg_sys(PurpleConnection *gc, guint8 code, guint32 from, guint16 seq)
 {
-	guint8 bar, *ack, *cursor;
+	qq_data *qd;
+	guint8 bar, *ack;
 	gchar *str;
 	gint ack_len, bytes;
 
+	qd = (qq_data *) gc->proto_data;
+	
 	str = g_strdup_printf("%d", from);
 	bar = 0x1e;
 	ack_len = 1 + 1 + strlen(str) + 1 + 2;
 	ack = g_newa(guint8, ack_len);
-	cursor = ack;
+
 	bytes = 0;
-
-	bytes += create_packet_b(ack, &cursor, code);
-	bytes += create_packet_b(ack, &cursor, bar);
-	bytes += create_packet_data(ack, &cursor, (guint8 *) str, strlen(str));
-	bytes += create_packet_b(ack, &cursor, bar);
-	bytes += create_packet_w(ack, &cursor, seq);
+	bytes += qq_put8(ack + bytes, code);
+	bytes += qq_put8(ack + bytes, bar);
+	bytes += qq_putdata(ack + bytes, (guint8 *) str, strlen(str));
+	bytes += qq_put8(ack + bytes, bar);
+	bytes += qq_put16(ack + bytes, seq);
 
 	g_free(str);
 
 	if (bytes == ack_len)	/* creation OK */
-		qq_send_cmd(gc, QQ_CMD_ACK_SYS_MSG, TRUE, 0, FALSE, ack, ack_len);
+		qq_send_cmd_detail(qd, QQ_CMD_ACK_SYS_MSG, 0, FALSE, ack, ack_len);
 	else
 		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
 			   "Fail creating sys msg ACK, expect %d bytes, build %d bytes\n", ack_len, bytes);
--- a/libpurple/protocols/qq/udp_proxy_s5.c	Wed Jul 09 00:27:44 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,381 +0,0 @@
-/**
- * @file udp_proxy_s5.c
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-
-#include "debug.h"
-
-#include "udp_proxy_s5.h"
-
-static void _qq_s5_canread_again(gpointer data, gint source, PurpleInputCondition cond)
-{
-	unsigned char buf[512];
-	struct PHB *phb = data;
-	struct sockaddr_in sin;
-	int len, error;
-	socklen_t errlen;
-	int flags;
-
-	purple_input_remove(phb->inpa);
-	purple_debug(PURPLE_DEBUG_INFO, "socks5 proxy", "Able to read again.\n");
-
-	len = read(source, buf, 10);
-	if (len < 10) {
-		purple_debug(PURPLE_DEBUG_WARNING, "socks5 proxy", "or not...\n");
-		close(source);
-
-		if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) {
-
-			phb->func(phb->data, source, NULL);
-		}
-
-		g_free(phb->host);
-		g_free(phb);
-		return;
-	}
-	if ((buf[0] != 0x05) || (buf[1] != 0x00)) {
-		if ((buf[0] == 0x05) && (buf[1] < 0x09))
-			purple_debug(PURPLE_DEBUG_ERROR, "socks5 proxy", "socks5 error: %x\n", buf[1]);
-		else
-			purple_debug(PURPLE_DEBUG_ERROR, "socks5 proxy", "Bad data.\n");
-		close(source);
-
-		if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) {
-
-			phb->func(phb->data, -1, _("Unable to connect"));
-		}
-
-		g_free(phb->host);
-		g_free(phb);
-		return;
-	}
-
-	sin.sin_family = AF_INET;
-	memcpy(&sin.sin_addr.s_addr, buf + 4, 4);
-	memcpy(&sin.sin_port, buf + 8, 2);
-
-	if (connect(phb->udpsock, (struct sockaddr *) &sin, sizeof(struct sockaddr_in)) < 0) {
-		purple_debug(PURPLE_DEBUG_INFO, "s5_canread_again", "connect failed: %s\n", g_strerror(errno));
-		close(phb->udpsock);
-		close(source);
-		g_free(phb->host);
-		g_free(phb);
-		return;
-	}
-
-	error = ETIMEDOUT;
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "Connect didn't block\n");
-	errlen = sizeof(error);
-	if (getsockopt(phb->udpsock, SOL_SOCKET, SO_ERROR, &error, &errlen) < 0) {
-		purple_debug(PURPLE_DEBUG_ERROR, "QQ", "getsockopt failed.\n");
-		close(phb->udpsock);
-		return;
-	}
-	flags = fcntl(phb->udpsock, F_GETFL);
-	fcntl(phb->udpsock, F_SETFL, flags & ~O_NONBLOCK);
-
-	if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) {
-		phb->func(phb->data, phb->udpsock, NULL);
-	}
-
-	g_free(phb->host);
-	g_free(phb);
-}
-
-static void _qq_s5_sendconnect(gpointer data, gint source)
-{
-	unsigned char buf[512];
-	struct PHB *phb = data;
-	struct sockaddr_in sin, ctlsin;
-	int port; 
-	socklen_t ctllen;
-	int flags;
-
-	purple_debug(PURPLE_DEBUG_INFO, "s5_sendconnect", "remote host is %s:%d\n", phb->host, phb->port);
-
-	buf[0] = 0x05;
-	buf[1] = 0x03;		/* udp relay */
-	buf[2] = 0x00;		/* reserved */
-	buf[3] = 0x01;		/* address type -- ipv4 */
-	memset(buf + 4, 0, 0x04);
-
-	ctllen = sizeof(ctlsin);
-	if (getsockname(source, (struct sockaddr *) &ctlsin, &ctllen) < 0) {
-		purple_debug(PURPLE_DEBUG_INFO, "QQ", "getsockname: %s\n", g_strerror(errno));
-		close(source);
-		g_free(phb->host);
-		g_free(phb);
-		return;
-	}
-
-	phb->udpsock = socket(PF_INET, SOCK_DGRAM, 0);
-	purple_debug(PURPLE_DEBUG_INFO, "s5_sendconnect", "UDP socket=%d\n", phb->udpsock);
-	if (phb->udpsock < 0) {
-		close(source);
-		g_free(phb->host);
-		g_free(phb);
-		return;
-	}
-
-	flags = fcntl(phb->udpsock, F_GETFL);
-	fcntl(phb->udpsock, F_SETFL, flags | O_NONBLOCK);
-
-	port = g_ntohs(ctlsin.sin_port) + 1;
-	while (1) {
-		inet_aton("0.0.0.0", &(sin.sin_addr));
-		sin.sin_family = AF_INET;
-		sin.sin_port = g_htons(port);
-		if (bind(phb->udpsock, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
-			port++;
-			if (port > 65500) {
-				close(source);
-				g_free(phb->host);
-				g_free(phb);
-				return;
-			}
-		} else
-			break;
-	}
-
-	memset(buf + 4, 0, 0x04);
-	memcpy(buf + 8, &(sin.sin_port), 0x02);
-
-	if (write(source, buf, 10) < 10) {
-		close(source);
-		purple_debug(PURPLE_DEBUG_INFO, "s5_sendconnect", "packet too small\n");
-
-		if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) {
-			phb->func(phb->data, -1, _("Unable to connect"));
-		}
-
-		g_free(phb->host);
-		g_free(phb);
-		return;
-	}
-
-	phb->inpa = purple_input_add(source, PURPLE_INPUT_READ, _qq_s5_canread_again, phb);
-}
-
-static void _qq_s5_readauth(gpointer data, gint source, PurpleInputCondition cond)
-{
-	unsigned char buf[512];
-	struct PHB *phb = data;
-
-	purple_input_remove(phb->inpa);
-	purple_debug(PURPLE_DEBUG_INFO, "socks5 proxy", "Got auth response.\n");
-
-	if (read(source, buf, 2) < 2) {
-		close(source);
-
-		if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) {
-
-			phb->func(phb->data, -1, _("Unable to connect"));
-		}
-
-		g_free(phb->host);
-		g_free(phb);
-		return;
-	}
-
-	if ((buf[0] != 0x01) || (buf[1] != 0x00)) {
-		close(source);
-
-		if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) {
-
-			phb->func(phb->data, -1, _("Unable to connect"));
-		}
-
-		g_free(phb->host);
-		g_free(phb);
-		return;
-	}
-
-	_qq_s5_sendconnect(phb, source);
-}
-
-static void _qq_s5_canread(gpointer data, gint source, PurpleInputCondition cond)
-{
-	unsigned char buf[512];
-	struct PHB *phb;
-	int ret;
-
-	phb = data;
-
-	purple_input_remove(phb->inpa);
-	purple_debug(PURPLE_DEBUG_INFO, "socks5 proxy", "Able to read.\n");
-
-	ret = read(source, buf, 2);
-	if (ret < 2) {
-		purple_debug(PURPLE_DEBUG_INFO, "s5_canread", "packet smaller than 2 octet\n");
-		close(source);
-
-		if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) {
-
-			phb->func(phb->data, -1, _("Unable to connect"));
-		}
-
-		g_free(phb->host);
-		g_free(phb);
-		return;
-	}
-
-	if ((buf[0] != 0x05) || (buf[1] == 0xff)) {
-		purple_debug(PURPLE_DEBUG_INFO, "s5_canread", "unsupport\n");
-		close(source);
-
-		if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) {
-
-			phb->func(phb->data, -1, _("Unable to connect"));
-		}
-
-		g_free(phb->host);
-		g_free(phb);
-		return;
-	}
-
-	if (buf[1] == 0x02) {
-		unsigned int i, j;
-
-		i = strlen(purple_proxy_info_get_username(phb->gpi));
-		j = strlen(purple_proxy_info_get_password(phb->gpi));
-
-		buf[0] = 0x01;	/* version 1 */
-		buf[1] = i;
-		memcpy(buf + 2, purple_proxy_info_get_username(phb->gpi), i);
-		buf[2 + i] = j;
-		memcpy(buf + 2 + i + 1, purple_proxy_info_get_password(phb->gpi), j);
-
-		if (write(source, buf, 3 + i + j) < 3 + i + j) {
-			close(source);
-
-			if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) {
-
-				phb->func(phb->data, -1, _("Unable to connect"));
-			}
-
-			g_free(phb->host);
-			g_free(phb);
-			return;
-		}
-
-		phb->inpa = purple_input_add(source, PURPLE_INPUT_READ, _qq_s5_readauth, phb);
-	} else {
-		purple_debug(PURPLE_DEBUG_INFO, "s5_canread", "calling s5_sendconnect\n");
-		_qq_s5_sendconnect(phb, source);
-	}
-}
-
-static void _qq_s5_canwrite(gpointer data, gint source, PurpleInputCondition cond)
-{
-	unsigned char buf[512];
-	int i;
-	struct PHB *phb = data;
-	socklen_t len;
-	int error = ETIMEDOUT;
-	int flags;
-
-	purple_debug(PURPLE_DEBUG_INFO, "socks5 proxy", "Connected.\n");
-
-	if (phb->inpa > 0)
-		purple_input_remove(phb->inpa);
-
-	len = sizeof(error);
-	if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
-		purple_debug(PURPLE_DEBUG_INFO, "getsockopt", "%s\n", g_strerror(errno));
-		close(source);
-		if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) {
-
-			phb->func(phb->data, -1, _("Unable to connect"));
-		}
-
-		g_free(phb->host);
-		g_free(phb);
-		return;
-	}
-	flags = fcntl(source, F_GETFL);
-	fcntl(source, F_SETFL, flags & ~O_NONBLOCK);
-
-	i = 0;
-	buf[0] = 0x05;		/* SOCKS version 5 */
-
-	if (purple_proxy_info_get_username(phb->gpi) != NULL) {
-		buf[1] = 0x02;	/* two methods */
-		buf[2] = 0x00;	/* no authentication */
-		buf[3] = 0x02;	/* username/password authentication */
-		i = 4;
-	} else {
-		buf[1] = 0x01;
-		buf[2] = 0x00;
-		i = 3;
-	}
-
-	if (write(source, buf, i) < i) {
-		purple_debug(PURPLE_DEBUG_INFO, "write", "%s\n", g_strerror(errno));
-		purple_debug(PURPLE_DEBUG_ERROR, "socks5 proxy", "Unable to write\n");
-		close(source);
-
-		if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) {
-
-			phb->func(phb->data, -1, _("Unable to connect"));
-		}
-
-		g_free(phb->host);
-		g_free(phb);
-		return;
-	}
-
-	phb->inpa = purple_input_add(source, PURPLE_INPUT_READ, _qq_s5_canread, phb);
-}
-
-gint qq_proxy_socks5(struct PHB *phb, struct sockaddr *addr, socklen_t addrlen)
-{
-	gint fd;
-	int flags;
-
-	purple_debug(PURPLE_DEBUG_INFO, "QQ",
-		   "Connecting to %s:%d via %s:%d using SOCKS5\n",
-		   phb->host, phb->port, purple_proxy_info_get_host(phb->gpi), purple_proxy_info_get_port(phb->gpi));
-
-	if ((fd = socket(addr->sa_family, SOCK_STREAM, 0)) < 0)
-		return -1;
-
-	purple_debug(PURPLE_DEBUG_INFO, "QQ", "proxy_sock5 return fd=%d\n", fd);
-
-	flags = fcntl(fd, F_GETFL);
-	fcntl(fd, F_SETFL, flags | O_NONBLOCK);
-	if (connect(fd, addr, addrlen) < 0) {
-		if ((errno == EINPROGRESS) || (errno == EINTR)) {
-			purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Connect in asynchronous mode.\n");
-			phb->inpa = purple_input_add(fd, PURPLE_INPUT_WRITE, _qq_s5_canwrite, phb);
-		} else {
-			close(fd);
-			return -1;
-		}
-	} else {
-		purple_debug(PURPLE_DEBUG_MISC, "QQ", "Connect in blocking mode.\n");
-		flags = fcntl(fd, F_GETFL);
-		fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
-		_qq_s5_canwrite(phb, fd, PURPLE_INPUT_WRITE);
-	}
-
-	return fd;
-}
--- a/libpurple/protocols/qq/udp_proxy_s5.h	Wed Jul 09 00:27:44 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,34 +0,0 @@
-/**
- * @file udp_proxy_s5.h
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-
-#ifndef _QQ_UDP_PROXY_S5_H_
-#define _QQ_UDP_PROXY_S5_H_
-
-#include "internal.h"		/* for socket stuff */
-
-#include "qq_proxy.h"
-
-gint qq_proxy_socks5(struct PHB *phb, struct sockaddr *addr, socklen_t addrlen);
-
-#endif
--- a/libpurple/protocols/qq/utils.c	Wed Jul 09 00:27:44 2008 +0000
+++ b/libpurple/protocols/qq/utils.c	Wed Jul 09 00:32:18 2008 +0000
@@ -22,7 +22,6 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
 
-#include "cipher.h"
 #include "limits.h"
 #include "stdlib.h"
 #include "string.h"
@@ -40,6 +39,17 @@
 
 #define QQ_NAME_FORMAT    "%d"
 
+/* These functions are used only in development phase */
+/*
+   static void _qq_show_socket(gchar *desc, gint fd) {
+   struct sockaddr_in sin;
+   socklen_t len = sizeof(sin);
+   getsockname(fd, (struct sockaddr *)&sin, &len);
+   purple_debug(PURPLE_DEBUG_INFO, desc, "%s:%d\n",
+   inet_ntoa(sin.sin_addr), g_ntohs(sin.sin_port));
+   }
+   */
+
 gchar *get_name_by_index_str(gchar **array, const gchar *index_str, gint amount)
 {
 	gint index;
@@ -113,26 +123,6 @@
 	return segments;
 }
 
-/* generate a md5 key using uid and session_key */
-guint8 *_gen_session_md5(gint uid, guint8 *session_key)
-{
-	guint8 *src, md5_str[QQ_KEY_LENGTH];
-	PurpleCipher *cipher;
-	PurpleCipherContext *context;
-
-	src = g_newa(guint8, 20);
-	memcpy(src, &uid, 4);
-	memcpy(src, session_key, QQ_KEY_LENGTH);
-
-	cipher = purple_ciphers_find_cipher("md5");
-	context = purple_cipher_context_new(cipher, NULL);
-	purple_cipher_context_append(context, src, 20);
-	purple_cipher_context_digest(context, sizeof(md5_str), md5_str, NULL);
-	purple_cipher_context_destroy(context);
-
-	return g_memdup(md5_str, QQ_KEY_LENGTH);
-}
-
 /* given a four-byte ip data, convert it into a human readable ip string
  * the return needs to be freed */
 gchar *gen_ip_str(guint8 *ip)
@@ -194,7 +184,7 @@
 }
 
 /* try to dump the data as GBK */
-void try_dump_as_gbk(const guint8 *const data, gint len)
+gchar* try_dump_as_gbk(const guint8 *const data, gint len)
 {
 	gint i;
 	guint8 *incoming;
@@ -215,8 +205,8 @@
 
 	if (msg_utf8 != NULL) {
 		purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Try extract GB msg: %s\n", msg_utf8);
-		g_free(msg_utf8);
 	}
+	return msg_utf8;
 }
 
 /* strips whitespace */
@@ -294,7 +284,7 @@
 
 /* Dumps a chunk of raw data into an ASCII hex string.
  * The return should be freed later. */
-gchar *hex_dump_to_str(const guint8 *const buffer, gint bytes)
+static gchar *hex_dump_to_str(const guint8 *const buffer, gint bytes)
 {
 	GString *str;
 	gchar *ret;
@@ -331,6 +321,51 @@
 	return ret;
 }
 
+void qq_hex_dump(PurpleDebugLevel level, const char *category,
+		const guint8 *pdata, gint bytes,	
+		const char *format, ...)
+{
+	va_list args;
+	char *arg_s = NULL;
+	gchar *phex = NULL;
+
+	g_return_if_fail(level != PURPLE_DEBUG_ALL);
+	g_return_if_fail(format != NULL);
+
+	va_start(args, format);
+	arg_s = g_strdup_vprintf(format, args);
+	va_end(args);
+
+	if (bytes <= 0) {
+		purple_debug(level, category, arg_s);
+		return;
+	}
+
+	phex = hex_dump_to_str(pdata, bytes);
+	purple_debug(level, category, "%s - (len %d)\n%s", arg_s, bytes, phex);
+	g_free(phex);
+}
+
+void qq_show_packet(const gchar *desc, const guint8 *buf, gint len)
+{
+	/*
+	   char buf1[8*len+2], buf2[10];
+	   int i;
+	   buf1[0] = 0;
+	   for (i = 0; i < len; i++) {
+	   sprintf(buf2, " %02x(%d)", buf[i] & 0xff, buf[i] & 0xff);
+	   strcat(buf1, buf2);
+	   }
+	   strcat(buf1, "\n");
+	   purple_debug(PURPLE_DEBUG_INFO, desc, "%s", buf1);
+	   */
+
+	/* modified by s3e, 20080424 */
+	qq_hex_dump(PURPLE_DEBUG_INFO, desc,
+		buf, len,
+		"");
+}
+
 /* convert face num from packet (0-299) to local face (1-100) */
 gchar *face_to_icon_str(gint face)
 {
--- a/libpurple/protocols/qq/utils.h	Wed Jul 09 00:27:44 2008 +0000
+++ b/libpurple/protocols/qq/utils.h	Wed Jul 09 00:32:18 2008 +0000
@@ -28,12 +28,13 @@
 #include <stdio.h>
 #include <glib.h>
 
+#include "debug.h"
+
 gchar *get_name_by_index_str(gchar **array, const gchar *index_str, gint amount);
 gchar *get_index_str_by_name(gchar **array, const gchar *name, gint amount);
 gint qq_string_to_dec_value(const gchar *str);
 
 gchar **split_data(guint8 *data, gint len, const gchar *delimit, gint expected_fields);
-guint8 *_gen_session_md5(gint uid, guint8 *session_key);
 
 gchar *gen_ip_str(guint8 *ip);
 guint8 *str_ip_gen(gchar *str);
@@ -44,10 +45,13 @@
 
 gchar *face_to_icon_str(gint face);
 
-void try_dump_as_gbk(const guint8 *const data, gint len);
+gchar *try_dump_as_gbk(const guint8 *const data, gint len);
 
+void qq_show_packet(const gchar *desc, const guint8 *buf, gint len);
+void qq_hex_dump(PurpleDebugLevel level, const char *category,
+		const guint8 *pdata, gint bytes,	
+		const char *format, ...);
 guint8 *hex_str_to_bytes(const gchar *buf, gint *out_len);
-gchar *hex_dump_to_str(const guint8 *buf, gint buf_len);
 
 const gchar *qq_buddy_icon_dir(void);
 const gchar *qq_win32_buddy_icon_dir(void);
--- a/libpurple/protocols/silc/buddy.c	Wed Jul 09 00:27:44 2008 +0000
+++ b/libpurple/protocols/silc/buddy.c	Wed Jul 09 00:32:18 2008 +0000
@@ -1434,13 +1434,25 @@
 void silcpurple_idle_set(PurpleConnection *gc, int idle)
 
 {
-	SilcPurple sg = gc->proto_data;
-	SilcClient client = sg->client;
-	SilcClientConnection conn = sg->conn;
+	SilcPurple sg;
+	SilcClient client;
+	SilcClientConnection conn;
 	SilcAttributeObjService service;
 	const char *server;
 	int port;
 
+	sg = gc->proto_data;
+	if (sg == NULL)
+		return;
+
+	client = sg->client;
+	if (client == NULL)
+		return;
+
+	conn = sg->conn;
+	if (conn == NULL)
+		return;
+
 	server = purple_account_get_string(sg->account, "server",
 					 "silc.silcnet.org");
 	port = purple_account_get_int(sg->account, "port", 706),
--- a/libpurple/protocols/silc10/buddy.c	Wed Jul 09 00:27:44 2008 +0000
+++ b/libpurple/protocols/silc10/buddy.c	Wed Jul 09 00:32:18 2008 +0000
@@ -1434,13 +1434,25 @@
 void silcpurple_idle_set(PurpleConnection *gc, int idle)
 
 {
-	SilcPurple sg = gc->proto_data;
-	SilcClient client = sg->client;
-	SilcClientConnection conn = sg->conn;
+	SilcPurple sg;
+	SilcClient client;
+	SilcClientConnection conn;
 	SilcAttributeObjService service;
 	const char *server;
 	int port;
 
+	sg = gc->proto_data;
+	if (sg == NULL)
+		return;
+
+	client = sg->client;
+	if (client == NULL)
+		return;
+
+	conn = sg->conn;
+	if (conn == NULL)
+		return;
+
 	server = purple_account_get_string(sg->account, "server",
 					 "silc.silcnet.org");
 	port = purple_account_get_int(sg->account, "port", 706),
--- a/libpurple/util.c	Wed Jul 09 00:27:44 2008 +0000
+++ b/libpurple/util.c	Wed Jul 09 00:32:18 2008 +0000
@@ -3599,7 +3599,7 @@
 static void url_fetch_connect_cb(gpointer url_data, gint source, const gchar *error_message);
 
 static gboolean
-parse_redirect(const char *data, size_t data_len, gint sock,
+parse_redirect(const char *data, size_t data_len,
 			   PurpleUtilFetchUrlData *gfud)
 {
 	gchar *s;
@@ -3773,7 +3773,7 @@
 					header_len, gfud->webdata);
 
 				/* See if we can find a redirect. */
-				if(parse_redirect(gfud->webdata, header_len, source, gfud))
+				if(parse_redirect(gfud->webdata, header_len, gfud))
 					return;
 
 				gfud->got_headers = TRUE;
--- a/libpurple/xmlnode.c	Wed Jul 09 00:27:44 2008 +0000
+++ b/libpurple/xmlnode.c	Wed Jul 09 00:32:18 2008 +0000
@@ -728,6 +728,13 @@
 	return ret;
 }
 
+static void
+xmlnode_copy_foreach_ns(gpointer key, gpointer value, gpointer user_data)
+{
+	GHashTable *ret = (GHashTable *)user_data;
+	g_hash_table_insert(ret, g_strdup(key), g_strdup(value));
+}
+
 xmlnode *
 xmlnode_copy(const xmlnode *src)
 {
@@ -739,17 +746,23 @@
 
 	ret = new_node(src->name, src->type);
 	ret->xmlns = g_strdup(src->xmlns);
-	if(src->data) {
-		if(src->data_sz) {
+	if (src->data) {
+		if (src->data_sz) {
 			ret->data = g_memdup(src->data, src->data_sz);
 			ret->data_sz = src->data_sz;
 		} else {
 			ret->data = g_strdup(src->data);
 		}
 	}
+	ret->prefix = g_strdup(src->prefix);
+	if (src->namespace_map) {
+		ret->namespace_map = g_hash_table_new_full(g_str_hash, g_str_equal,
+		                                           g_free, g_free);
+		g_hash_table_foreach(src->namespace_map, xmlnode_copy_foreach_ns, ret->namespace_map);
+	}
 
-	for(child = src->child; child; child = child->next) {
-		if(sibling) {
+	for (child = src->child; child; child = child->next) {
+		if (sibling) {
 			sibling->next = xmlnode_copy(child);
 			sibling = sibling->next;
 		} else {
--- a/pidgin/gtkblist.c	Wed Jul 09 00:27:44 2008 +0000
+++ b/pidgin/gtkblist.c	Wed Jul 09 00:32:18 2008 +0000
@@ -130,6 +130,7 @@
 
 static guint visibility_manager_count = 0;
 static GdkVisibilityState gtk_blist_visibility = GDK_VISIBILITY_UNOBSCURED;
+static gboolean gtk_blist_focused = FALSE;
 static gboolean editing_blist = FALSE;
 
 static GList *pidgin_blist_sort_methods = NULL;
@@ -5166,9 +5167,14 @@
 /******************************************/
 
 static int
-blist_focus_cb(GtkWidget *widget, gpointer data, PidginBuddyList *gtkblist)
-{
-	pidgin_set_urgent(GTK_WINDOW(gtkblist->window), FALSE);
+blist_focus_cb(GtkWidget *widget, GdkEventFocus *event, PidginBuddyList *gtkblist)
+{
+	if(event->in) {
+		gtk_blist_focused = TRUE;
+		pidgin_set_urgent(GTK_WINDOW(gtkblist->window), FALSE);
+	} else {
+		gtk_blist_focused = FALSE;
+	}
 	return 0;
 }
 
@@ -5255,6 +5261,8 @@
 	gtkblist->window = pidgin_create_window(_("Buddy List"), 0, "buddy_list", TRUE);
 	g_signal_connect(G_OBJECT(gtkblist->window), "focus-in-event",
 			 G_CALLBACK(blist_focus_cb), gtkblist);
+	g_signal_connect(G_OBJECT(gtkblist->window), "focus-out-event",
+			 G_CALLBACK(blist_focus_cb), gtkblist);
 	GTK_WINDOW(gtkblist->window)->allow_shrink = TRUE;
 
 	gtkblist->main_vbox = gtk_vbox_new(FALSE, 0);
@@ -6988,8 +6996,15 @@
 {
 	if (gtkblist && gtkblist->window) {
 		if (GTK_WIDGET_VISIBLE(gtkblist->window)) {
+			/* make the buddy list visible if it is iconified or if it is
+			 * obscured and not currently focused (the focus part ensures
+			 * that we do something reasonable if the buddy list is obscured
+			 * by a window set to always be on top), otherwise hide the
+			 * buddy list
+			 */
 			purple_blist_set_visible(PIDGIN_WINDOW_ICONIFIED(gtkblist->window) ||
-					gtk_blist_visibility != GDK_VISIBILITY_UNOBSCURED);
+					((gtk_blist_visibility != GDK_VISIBILITY_UNOBSCURED) &&
+					!gtk_blist_focused));
 		} else {
 			purple_blist_set_visible(TRUE);
 		}
--- a/pidgin/gtkimhtml.c	Wed Jul 09 00:27:44 2008 +0000
+++ b/pidgin/gtkimhtml.c	Wed Jul 09 00:32:18 2008 +0000
@@ -768,7 +768,7 @@
 	gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(widget), &end,
 	                                   buf_x + event->area.width, buf_y + event->area.height);
 
-
+	gtk_text_iter_order(&start, &end);
 
 	cur = start;
 
--- a/pidgin/gtkmenutray.c	Wed Jul 09 00:27:44 2008 +0000
+++ b/pidgin/gtkmenutray.c	Wed Jul 09 00:32:18 2008 +0000
@@ -98,8 +98,9 @@
 		gtk_widget_destroy(GTK_WIDGET(tray->tray));
 #endif
 
-	if (tray->tooltips)
-		g_object_ref_sink(G_OBJECT(tray->tooltips));
+	if (tray->tooltips) {
+		gtk_object_sink(GTK_OBJECT(tray->tooltips));
+	}
 
 	G_OBJECT_CLASS(parent_class)->finalize(obj);
 }
--- a/pidgin/win32/nsis/pidgin-installer.nsi	Wed Jul 09 00:27:44 2008 +0000
+++ b/pidgin/win32/nsis/pidgin-installer.nsi	Wed Jul 09 00:32:18 2008 +0000
@@ -1388,67 +1388,6 @@
 
 FunctionEnd
 
-; This is a modified StartRadioButtons (from Sections.nsh)
-; The only difference is that it allows for nothing in the group to be selected
-; In that case, the default variable should be set to ""
-!macro StartRadioButtonsUnselectable var
-
-  !define StartRadioButtons_Var "${var}"
-
-  Push $R0
-  Push $R1
-
-   ;If we have no selection, don't try to unselect it
-   StrCmp "${StartRadioButtons_Var}" "" +4
-   SectionGetFlags "${StartRadioButtons_Var}" $R0
-   IntOp $R1 $R0 & ${SF_SELECTED}
-   IntOp $R0 $R0 & ${SECTION_OFF}
-   SectionSetFlags "${StartRadioButtons_Var}" $R0
-
-   ; If the previous value isn't currently selected,
-   ; we don't want to select it at the end
-   IntCmp $R1 ${SF_SELECTED} +2
-   StrCpy "${StartRadioButtons_Var}" ""
-
-   StrCpy $R1 "${StartRadioButtons_Var}"
-
-!macroend
-
-Function .onSelChange
-  Push $0
-  Push $1
-  Push $2
-
-  ; Check that at most one of the non-readonly spelling dictionaries are selected
-  ; We can't use $R0 or $R1 in this block since they're used in the macros
-  !insertmacro StartRadioButtonsUnselectable $SPELLCHECK_SEL
-    ; Start with the first language dictionary
-    IntOp $2 ${SecSpellCheck} + 1
-
-    start_spellcheck_radio:
-    SectionGetFlags $2 $0
-
-    IntOp $1 $0 & ${SF_SECGRPEND}
-    ; If it is the end of the section group, stop
-    IntCmp $1 ${SF_SECGRPEND} end_spellcheck_radio
-
-    IntOp $0 $0 & ${SF_RO}
-    IntCmp $0 ${SF_RO} after_button_insert
-    ; If !readonly, then it is part of the radiobutton group
-    !insertmacro RadioButton $2
-    after_button_insert:
-
-    IntOp $2 $2 + 1 ;Advance to the next section
-    Goto start_spellcheck_radio
-
-    end_spellcheck_radio:
-  !insertmacro EndRadioButtons
-
-  Pop $2
-  Pop $1
-  Pop $0
-FunctionEnd
-
 ; Page enter and exit functions..
 
 Function preWelcomePage
--- a/po/de.po	Wed Jul 09 00:27:44 2008 +0000
+++ b/po/de.po	Wed Jul 09 00:32:18 2008 +0000
@@ -7,14 +7,13 @@
 #
 # This file is distributed under the same license as the Pidgin package.
 #
-# Bjoern Voigt <bjoern@cs.tu-berlin.de>, 2008.
 msgid ""
 msgstr ""
 "Project-Id-Version: de\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2008-06-12 18:33+0200\n"
-"PO-Revision-Date: 2008-06-12 18:32+0200\n"
-"Last-Translator: Jochen Kemnade <jochenkemnade@web.de>\n"
+"POT-Creation-Date: 2008-06-25 23:30+0200\n"
+"PO-Revision-Date: 2008-06-25 23:27+0200\n"
+"Last-Translator: Bjoern Voigt <bjoern@cs.tu-berlin.de>\n"
 "Language-Team: Deutsch <de@li.org>\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
@@ -622,7 +621,6 @@
 msgid "Invite"
 msgstr "Einladen"
 
-#, fuzzy
 msgid ""
 "Please enter the name of the user you wish to invite,\n"
 "along with an optional invite message."
@@ -6282,9 +6280,8 @@
 msgid "Camera"
 msgstr "Kamera"
 
-#, fuzzy
 msgid "Screen Sharing"
-msgstr "Screen Sharing"
+msgstr "Gemeinsamer Bildschirm"
 
 msgid "Free For Chat"
 msgstr "Bereit zum Chatten"
@@ -9766,7 +9763,6 @@
 msgid "Stored Image"
 msgstr "Gespeichertes Bild"
 
-#, fuzzy
 msgid "Stored Image. (that'll have to do for now)"
 msgstr "Gespeichertes Bild. (Das muss erstmal reichen)"
 
@@ -11446,9 +11442,8 @@
 msgid "Color to draw the name of an action message."
 msgstr "Farbe, mit der der Name in einer Aktions-Nachricht dargestellt wird."
 
-#, fuzzy
 msgid "Action Message Name Color for Whispered Message"
-msgstr "Farbe des Absendernamens für Aktions-Nachrichten"
+msgstr "Farbe des Absendernamens für geflüsterte Aktions-Nachrichten"
 
 msgid "Whisper Message Name Color"
 msgstr "Farbe des Absendernamens für Flüster-Nachrichten"
@@ -11523,7 +11518,6 @@
 msgid "_Save Image..."
 msgstr "Bild _speichern..."
 
-#, fuzzy
 msgid "_Add Custom Smiley..."
 msgstr "Benutzerdefinierten Smiley _hinzufügen..."
 
@@ -11577,7 +11571,6 @@
 msgid "Smile!"
 msgstr "Lächeln!"
 
-#, fuzzy
 msgid "_Manage custom smileys"
 msgstr "Benutzerdefinierte Smileys _verwalten"
 
--- a/po/it.po	Wed Jul 09 00:27:44 2008 +0000
+++ b/po/it.po	Wed Jul 09 00:32:18 2008 +0000
@@ -3833,8 +3833,8 @@
 msgstr "Impossibile trovare un'installazione di ActiveTCL. Se vuoi usare i plugin TCL, installa ActiveTCL da http://www.activestate.com\n"
 
 #: ../libpurple/protocols/bonjour/bonjour.c:107
-msgid "The Apple Bonjour For Windows toolkit wasn't found, see the FAQ at: http://developer.pidgin.im/wiki/Using%20Pidgin#CanIusePidginforBonjourLink-LocalMessaging for more information."
-msgstr "Il toolkit Apple Bonjour per Windows non è stato trovato. Leggi le FAQ su: http://developer.pidgin.im/wiki/Using%20Pidgin#CanIusePidginforBonjourLink-LocalMessaging per maggiori informazioni."
+msgid "The Apple Bonjour For Windows toolkit wasn't found, see the FAQ at: http://d.pidgin.im/BonjourWindows for more information."
+msgstr "Il toolkit Apple Bonjour per Windows non è stato trovato. Leggi le FAQ su: http://d.pidgin.im/BonjourWindows per maggiori informazioni."
 
 #: ../libpurple/protocols/bonjour/bonjour.c:126
 msgid "Unable to listen for incoming IM connections\n"
--- a/po/nb.po	Wed Jul 09 00:27:44 2008 +0000
+++ b/po/nb.po	Wed Jul 09 00:32:18 2008 +0000
@@ -3877,7 +3877,7 @@
 msgstr "Kunne ikke finne en ActiveTCL installasjon. Om du ønsker å bruke TCL tillegg, installer ActiveTCL fra http://www.activestate.com\n"
 
 #: ../libpurple/protocols/bonjour/bonjour.c:101
-msgid "The Apple Bonjour For Windows toolkit wasn't found, see the FAQ at: http://developer.pidgin.im/wiki/Using%20Pidgin#CanIusePidginforBonjourLink-LocalMessaging for more information."
+msgid "The Apple Bonjour For Windows toolkit wasn't found, see the FAQ at: http://d.pidgin.im/BonjourWindows for more information."
 msgstr ""
 
 #: ../libpurple/protocols/bonjour/bonjour.c:120
--- a/po/te.po	Wed Jul 09 00:27:44 2008 +0000
+++ b/po/te.po	Wed Jul 09 00:32:18 2008 +0000
@@ -4059,7 +4059,7 @@
 msgstr "ActiveTCL ఇన్స్టాలేషన్ ను కనుగొనడంలో అశక్తత. మీరు TCL ప్లగ్ ఇన్లను ఉపయోగించాలనుకుంటే http://www.activestate.com\n"
 
 #: ../libpurple/protocols/bonjour/bonjour.c:108
-msgid "The Apple Bonjour For Windows toolkit wasn't found, see the FAQ at: http://developer.pidgin.im/wiki/Using%20Pidgin#CanIusePidginforBonjourLink-LocalMessaging for more information."
+msgid "The Apple Bonjour For Windows toolkit wasn't found, see the FAQ at: http://d.pidgin.im/BonjourWindows for more information."
 msgstr ""
 
 #: ../libpurple/protocols/bonjour/bonjour.c:127
--- a/po/ur.po	Wed Jul 09 00:27:44 2008 +0000
+++ b/po/ur.po	Wed Jul 09 00:32:18 2008 +0000
@@ -3891,7 +3891,7 @@
 msgstr "ایكٹیو TCL ا نسٹالیشن كی  جانچ  نا ممكن۔ اگر آپ TCL پلگ انس  استعمال كرنا چاہتے ہیں ، ایكٹیوTCL انسٹال كروfrom http://www.activestate.com\n"
 
 #: ../libpurple/protocols/bonjour/bonjour.c:108
-msgid "The Apple Bonjour For Windows toolkit wasn't found, see the FAQ at: http://developer.pidgin.im/wiki/Using%20Pidgin#CanIusePidginforBonjourLink-LocalMessaging for more information."
+msgid "The Apple Bonjour For Windows toolkit wasn't found, see the FAQ at: http://d.pidgin.im/BonjourWindows for more information."
 msgstr ""
 
 #: ../libpurple/protocols/bonjour/bonjour.c:127
--- a/po/vi.po	Wed Jul 09 00:27:44 2008 +0000
+++ b/po/vi.po	Wed Jul 09 00:32:18 2008 +0000
@@ -3916,7 +3916,7 @@
 "The Apple Bonjour For Windows toolkit wasn't found, see the FAQ at: http://"
 "developer.pidgin.im/wiki/Using%20Pidgin#CanIusePidginforBonjourLink-"
 "LocalMessaging for more information."
-msgstr "Không tìm thấy bộ công cụ Apple Bonjour For Windows, xem FAQ (Hỏi Đáp) ở địa chỉ « http://developer.pidgin.im/wiki/Using%20Pidgin#CanIusePidginforBonjourLink-LocalMessaging » để tìm chi tiết."
+msgstr "Không tìm thấy bộ công cụ Apple Bonjour For Windows, xem FAQ (Hỏi Đáp) ở địa chỉ « http://d.pidgin.im/BonjourWindows » để tìm chi tiết."
 
 #: ../libpurple/protocols/bonjour/bonjour.c:120
 msgid "Unable to listen for incoming IM connections\n"