libpurple/protocols/qq/qq_process.c

branch
cpw.masca.webkit
changeset 32503
ab886d3a38ae
parent 32502
e64e49502c79
parent 31944
77d17906f1c3
child 32504
8243b910ed4c
--- a/libpurple/protocols/qq/qq_process.c	Thu Jun 16 04:37:03 2011 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1173 +0,0 @@
-/**
- * @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 "internal.h"
-#include "cipher.h"
-#include "debug.h"
-
-#include "buddy_info.h"
-#include "buddy_list.h"
-#include "buddy_opt.h"
-#include "group_info.h"
-#include "char_conv.h"
-#include "qq_crypt.h"
-
-#include "group_internal.h"
-#include "group_im.h"
-#include "group_info.h"
-#include "group_join.h"
-#include "group_opt.h"
-
-#include "qq_define.h"
-#include "qq_base.h"
-#include "im.h"
-#include "qq_process.h"
-#include "packet_parse.h"
-#include "qq_network.h"
-#include "qq_trans.h"
-#include "utils.h"
-#include "buddy_memo.h"
-
-enum {
-	QQ_ROOM_CMD_REPLY_OK = 0x00,
-	QQ_ROOM_CMD_REPLY_SEARCH_ERROR = 0x02,
-	QQ_ROOM_CMD_REPLY_NOT_MEMBER = 0x0a
-};
-
-/* default process, decrypt and dump */
-static void process_unknow_cmd(PurpleConnection *gc,const gchar *title, guint8 *data, gint data_len, guint16 cmd, guint16 seq)
-{
-	qq_data *qd;
-	gchar *msg;
-
-	g_return_if_fail(data != NULL && data_len != 0);
-
-	qq_show_packet(title, data, data_len);
-
-	qd = (qq_data *) gc->proto_data;
-
-	qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ",
-			data, data_len,
-			">>> [%d] %s -> [default] decrypt and dump",
-			seq, qq_get_cmd_desc(cmd));
-
-	msg = g_strdup_printf("Unknow command 0x%02X, %s", cmd, qq_get_cmd_desc(cmd));
-	purple_notify_info(gc, _("QQ Error"), title, msg);
-	g_free(msg);
-}
-
-/* parse the reply to send_im */
-static void do_im_ack(guint8 *data, gint data_len, PurpleConnection *gc)
-{
-	qq_data *qd;
-
-	g_return_if_fail(data != NULL && data_len != 0);
-
-	qd = gc->proto_data;
-
-	if (data[0] != 0) {
-		purple_debug_warning("QQ", "Failed sent IM\n");
-		purple_notify_error(gc, _("Error"), _("Unable to send message."), NULL);
-		return;
-	}
-
-	purple_debug_info("QQ", "OK sent IM\n");
-}
-
-static void do_server_news(PurpleConnection *gc, guint8 *data, gint data_len)
-{
-	qq_data *qd = (qq_data *) gc->proto_data;
-	gint bytes;
-	gchar *title, *brief, *url;
-	gchar *content;
-
-	g_return_if_fail(data != NULL && data_len != 0);
-
-	/* qq_show_packet("Rcv news", data, data_len); */
-
-	bytes = 4;	/* skip unknown 4 bytes */
-
-	bytes += qq_get_vstr(&title, QQ_CHARSET_DEFAULT, data + bytes);
-	bytes += qq_get_vstr(&brief, QQ_CHARSET_DEFAULT, data + bytes);
-	bytes += qq_get_vstr(&url, QQ_CHARSET_DEFAULT, data + bytes);
-
-	content = g_strdup_printf(_("Server News:\n%s\n%s\n%s"), title, brief, url);
-
-	if (qd->is_show_news) {
-		qq_got_message(gc, content);
-	} else {
-		purple_debug_info("QQ", "QQ Server news:\n%s\n", content);
-	}
-	g_free(title);
-	g_free(brief);
-	g_free(url);
-	g_free(content);
-}
-
-static void do_got_sms(PurpleConnection *gc, guint8 *data, gint data_len)
-{
-	gint bytes;
-	gchar *mobile = NULL;
-	gchar *msg = NULL;
-	gchar *msg_utf8 = NULL;
-	gchar *msg_formated;
-
-	g_return_if_fail(data != NULL && data_len > 26);
-
-	qq_show_packet("Rcv sms", data, data_len);
-
-	bytes = 0;
-	bytes += 1;	/* skip 0x00 */
-	mobile = g_strndup((gchar *)data + bytes, 20);
-	bytes += 20;
-	bytes += 5; /* skip 0x(49 11 98 d5 03)*/
-	if (bytes < data_len) {
-		msg = g_strndup((gchar *)data + bytes, data_len - bytes);
-		msg_utf8 = qq_to_utf8(msg, QQ_CHARSET_DEFAULT);
-		g_free(msg);
-	} else {
-		msg_utf8 = g_strdup("");
-	}
-
-	msg_formated = g_strdup_printf(_("%s:%s"), mobile, msg_utf8);
-
-	qq_got_message(gc, msg_formated);
-
-	g_free(msg_formated);
-	g_free(msg_utf8);
-	g_free(mobile);
-}
-
-static void do_msg_sys_30(PurpleConnection *gc, guint8 *data, gint data_len)
-{
-	gint len;
-	guint8 reply;
-	gchar **segments, *msg_utf8;
-
-	g_return_if_fail(data != NULL && data_len != 0);
-
-	len = data_len;
-
-	if (NULL == (segments = split_data(data, len, "\x2f", 2)))
-		return;
-
-	reply = strtol(segments[0], NULL, 10);
-	if (reply == 1)
-		purple_debug_warning("QQ", "We are kicked out by QQ server\n");
-
-	msg_utf8 = qq_to_utf8(segments[1], QQ_CHARSET_DEFAULT);
-	qq_got_message(gc, msg_utf8);
-}
-
-static void do_msg_sys_4c(PurpleConnection *gc, guint8 *data, gint data_len)
-{
-	gint bytes;
-	gint msg_len;
-	GString *content;
-	gchar *msg = NULL;
-
-	g_return_if_fail(data != NULL && data_len > 0);
-
-	bytes = 6; /* skip 0x(06 00 01 1e 01 1c)*/
-
-	content = g_string_new("");
-	while (bytes < data_len) {
-		msg_len = qq_get_vstr(&msg, QQ_CHARSET_DEFAULT, data + bytes);
-		g_string_append(content, msg);
-		g_string_append(content, "\n");
-		g_free(msg);
-
-		if (msg_len <= 1) {
-			break;
-		}
-		bytes += msg_len;
-	}
-	if (bytes != data_len) {
-		purple_debug_warning("QQ", "Failed to read QQ_MSG_SYS_4C\n");
-		qq_show_packet("do_msg_sys_4c", data, data_len);
-	}
-	qq_got_message(gc, content->str);
-	g_string_free(content, FALSE);
-}
-
-static const gchar *get_im_type_desc(gint type)
-{
-	switch (type) {
-		case QQ_MSG_TO_BUDDY:
-			return "QQ_MSG_TO_BUDDY";
-		case QQ_MSG_TO_UNKNOWN:
-			return "QQ_MSG_TO_UNKNOWN";
-		case QQ_MSG_QUN_IM_UNKNOWN:
-			return "QQ_MSG_QUN_IM_UNKNOWN";
-		case QQ_MSG_ADD_TO_QUN:
-			return "QQ_MSG_ADD_TO_QUN";
-		case QQ_MSG_DEL_FROM_QUN:
-			return "QQ_MSG_DEL_FROM_QUN";
-		case QQ_MSG_APPLY_ADD_TO_QUN:
-			return "QQ_MSG_APPLY_ADD_TO_QUN";
-		case QQ_MSG_CREATE_QUN:
-			return "QQ_MSG_CREATE_QUN";
-		case QQ_MSG_SYS_30:
-			return "QQ_MSG_SYS_30";
-		case QQ_MSG_SYS_4C:
-			return "QQ_MSG_SYS_4C";
-		case QQ_MSG_APPROVE_APPLY_ADD_TO_QUN:
-			return "QQ_MSG_APPROVE_APPLY_ADD_TO_QUN";
-		case QQ_MSG_REJCT_APPLY_ADD_TO_QUN:
-			return "QQ_MSG_REJCT_APPLY_ADD_TO_QUN";
-		case QQ_MSG_TEMP_QUN_IM:
-			return "QQ_MSG_TEMP_QUN_IM";
-		case QQ_MSG_QUN_IM:
-			return "QQ_MSG_QUN_IM";
-		case QQ_MSG_NEWS:
-			return "QQ_MSG_NEWS";
-		case QQ_MSG_SMS:
-			return "QQ_MSG_SMS";
-		case QQ_MSG_EXTEND:
-			return "QQ_MSG_EXTEND";
-		case QQ_MSG_EXTEND_85:
-			return "QQ_MSG_EXTEND_85";
-		default:
-			return "QQ_MSG_UNKNOWN";
-	}
-}
-
-/* I receive a message, mainly it is text msg,
- * but we need to proess other types (group etc) */
-static void process_private_msg(guint8 *data, gint data_len, guint16 seq, PurpleConnection *gc)
-{
-	qq_data *qd;
-	gint bytes;
-
-	struct {
-		guint32 uid_from;
-		guint32 uid_to;
-		guint32 seq;
-		struct in_addr ip_from;
-		guint16 port_from;
-		guint16 msg_type;
-	} header;
-
-	g_return_if_fail(data != NULL && data_len != 0);
-
-	qd = (qq_data *) gc->proto_data;
-
-	if (data_len < 16) {	/* we need to ack with the first 16 bytes */
-		purple_debug_error("QQ", "MSG is too short\n");
-		return;
-	} else {
-		/* when we receive a message,
-		 * we send an ACK which is the first 16 bytes of incoming packet */
-		qq_send_server_reply(gc, QQ_CMD_RECV_IM, seq, data, 16);
-	}
-
-	/* check len first */
-	if (data_len < 20) {	/* length of im_header */
-		purple_debug_error("QQ", "Invald MSG header, len %d < 20\n", data_len);
-		return;
-	}
-
-	bytes = 0;
-	bytes += qq_get32(&(header.uid_from), data + bytes);
-	bytes += qq_get32(&(header.uid_to), data + bytes);
-	bytes += qq_get32(&(header.seq), data + bytes);
-	/* if the message is delivered via server, it is server IP/port */
-	bytes += qq_getIP(&(header.ip_from), data + bytes);
-	bytes += qq_get16(&(header.port_from), data + bytes);
-	bytes += qq_get16(&(header.msg_type), data + bytes);
-	/* im_header prepared */
-
-	if (header.uid_to != qd->uid) {	/* should not happen */
-		purple_debug_error("QQ", "MSG to %u, NOT me\n", header.uid_to);
-		return;
-	}
-
-	/* check bytes */
-	if (bytes >= data_len - 1) {
-		purple_debug_warning("QQ", "Empty MSG\n");
-		return;
-	}
-
-	switch (header.msg_type) {
-		case QQ_MSG_NEWS:
-			do_server_news(gc, data + bytes, data_len - bytes);
-			break;
-		case QQ_MSG_SMS:
-			do_got_sms(gc, data + bytes, data_len - bytes);
-			break;
-		case QQ_MSG_EXTEND:
-		case QQ_MSG_EXTEND_85:
-			purple_debug_info("QQ", "MSG from buddy [%d]\n", header.uid_from);
-			qq_process_extend_im(gc, data + bytes, data_len - bytes);
-			break;
-		case QQ_MSG_TO_UNKNOWN:
-		case QQ_MSG_TO_BUDDY:
-			purple_debug_info("QQ", "MSG from buddy [%d]\n", header.uid_from);
-			qq_process_im(gc, data + bytes, data_len - bytes);
-			break;
-		case QQ_MSG_QUN_IM_UNKNOWN:
-		case QQ_MSG_TEMP_QUN_IM:
-		case QQ_MSG_QUN_IM:
-			purple_debug_info("QQ", "MSG from room [%d]\n", header.uid_from);
-			qq_process_room_im(data + bytes, data_len - bytes, header.uid_from, gc, header.msg_type);
-			break;
-		case QQ_MSG_ADD_TO_QUN:
-			purple_debug_info("QQ", "Notice from [%d], Added\n", header.uid_from);
-			/* uid_from is group id
-			 * we need this to create a dummy group and add to blist */
-			qq_process_room_buddy_joined(data + bytes, data_len - bytes, header.uid_from, gc);
-			break;
-		case QQ_MSG_DEL_FROM_QUN:
-			purple_debug_info("QQ", "Notice from room [%d], Removed\n", header.uid_from);
-			/* uid_from is group id */
-			qq_process_room_buddy_removed(data + bytes, data_len - bytes, header.uid_from, gc);
-			break;
-		case QQ_MSG_APPLY_ADD_TO_QUN:
-			purple_debug_info("QQ", "Notice from room [%d], Joined\n", header.uid_from);
-			/* uid_from is group id */
-			qq_process_room_buddy_request_join(data + bytes, data_len - bytes, header.uid_from, gc);
-			break;
-		case QQ_MSG_APPROVE_APPLY_ADD_TO_QUN:
-			purple_debug_info("QQ", "Notice from room [%d], Confirm add in\n",
-					header.uid_from);
-			/* uid_from is group id */
-			qq_process_room_buddy_approved(data + bytes, data_len - bytes, header.uid_from, gc);
-			break;
-		case QQ_MSG_REJCT_APPLY_ADD_TO_QUN:
-			purple_debug_info("QQ", "Notice from room [%d], Refuse add in\n",
-					header.uid_from);
-			/* uid_from is group id */
-			qq_process_room_buddy_rejected(data + bytes, data_len - bytes, header.uid_from, gc);
-			break;
-		case QQ_MSG_SYS_30:
-			do_msg_sys_30(gc, data + bytes, data_len - bytes);
-			break;
-		case QQ_MSG_SYS_4C:
-			do_msg_sys_4c(gc, data + bytes, data_len - bytes);
-			break;
-		default:
-			purple_debug_warning("QQ", "MSG from %u, unknown type %s [0x%04X]\n",
-					header.uid_from, get_im_type_desc(header.msg_type), header.msg_type);
-			qq_show_packet("MSG header", data, bytes);
-			if (data_len - bytes > 0) {
-				qq_show_packet("MSG data", data + bytes, data_len - bytes);
-			}
-			break;
-	}
-}
-
-/* Send ACK if the sys message needs an ACK */
-static void request_server_ack(PurpleConnection *gc, gchar *funct_str, gchar *from, guint16 seq)
-{
-	qq_data *qd;
-	guint8 *raw_data;
-	gint bytes;
-	guint8 bar;
-
-	g_return_if_fail(funct_str != NULL && from != NULL);
-	qd = (qq_data *) gc->proto_data;
-
-
-	bar = 0x1e;
-	raw_data = g_newa(guint8, strlen(funct_str) + strlen(from) + 16);
-
-	bytes = 0;
-	bytes += qq_putdata(raw_data + bytes, (guint8 *)funct_str, strlen(funct_str));
-	bytes += qq_put8(raw_data + bytes, bar);
-	bytes += qq_putdata(raw_data + bytes, (guint8 *)from, strlen(from));
-	bytes += qq_put8(raw_data + bytes, bar);
-	bytes += qq_put16(raw_data + bytes, seq);
-
-	qq_send_server_reply(gc, QQ_CMD_ACK_SYS_MSG, 0, raw_data, bytes);
-}
-
-static void do_server_notice(PurpleConnection *gc, gchar *from, gchar *to,
-		guint8 *data, gint data_len)
-{
-	qq_data *qd = (qq_data *) gc->proto_data;
-	gchar *msg, *msg_utf8;
-	gchar *title, *content;
-
-	g_return_if_fail(from != NULL && to != NULL && data_len > 0);
-
-	msg = g_strndup((gchar *)data, data_len);
-	msg_utf8 = qq_to_utf8(msg, QQ_CHARSET_DEFAULT);
-	g_free(msg);
-	if (msg_utf8 == NULL) {
-		purple_debug_error("QQ", "Recv NULL sys msg from %s to %s, discard\n",
-				from, to);
-		return;
-	}
-
-	title = g_strdup_printf(_("From %s:"), from);
-	content = g_strdup_printf(_("Server notice From %s: \n%s"), from, msg_utf8);
-
-	if (qd->is_show_notice) {
-		qq_got_message(gc, content);
-	} else {
-		purple_debug_info("QQ", "QQ Server notice from %s:\n%s\n", from, msg_utf8);
-	}
-	g_free(msg_utf8);
-	g_free(title);
-	g_free(content);
-}
-
-static void process_server_msg(PurpleConnection *gc, guint8 *data, gint data_len, guint16 seq)
-{
-	qq_data *qd;
-	guint8 *data_str, i = 0;
-	gchar **segments, **seg;
-	gchar *funct_str, *from, *to;
-	gint bytes, funct;
-
-	g_return_if_fail(data != NULL && data_len != 0);
-
-	qd = (qq_data *) gc->proto_data;
-
-	data_str = g_newa(guint8, data_len + 1);
-	g_memmove(data_str, data, data_len);
-	data_str[data_len] = 0x00;
-
-	segments = g_strsplit((gchar *) data_str, "\x1f", 0);
-	g_return_if_fail(segments != NULL);
-	for (seg = segments; *seg != NULL; seg++)
-		i++;
-	if (i < 3) {
-		purple_debug_warning("QQ", "Server message segments is less than 3\n");
-		g_strfreev(segments);
-		return;
-	}
-
-	bytes = 0;
-	funct_str = segments[0];
-	bytes += strlen(funct_str) + 1;
-	from = segments[1];
-	bytes += strlen(from) + 1;
-	to = segments[2];
-	bytes += strlen(to) + 1;
-
-	request_server_ack(gc, funct_str, from, seq);
-
-	/* qq_show_packet("Server MSG", data, data_len); */
-	if (strtoul(to, NULL, 10) != qd->uid) {	/* not to me */
-		purple_debug_error("QQ", "Recv sys msg to [%s], not me!, discard\n", to);
-		g_strfreev(segments);
-		return;
-	}
-
-	funct = strtol(funct_str, NULL, 10);
-	switch (funct) {
-		case QQ_SERVER_BUDDY_ADDED:
-		case QQ_SERVER_BUDDY_ADD_REQUEST:
-		case QQ_SERVER_BUDDY_ADDED_ME:
-		case QQ_SERVER_BUDDY_REJECTED_ME:
-		case QQ_SERVER_BUDDY_ADD_REQUEST_EX:
-		case QQ_SERVER_BUDDY_ADDING_EX:
-		case QQ_SERVER_BUDDY_ADDED_ANSWER:
-		case QQ_SERVER_BUDDY_ADDED_EX:
-			qq_process_buddy_from_server(gc,  funct, from, to, data + bytes, data_len - bytes);
-			break;
-		case QQ_SERVER_NOTICE:
-			do_server_notice(gc, from, to, data + bytes, data_len - bytes);
-			break;
-		case QQ_SERVER_NEW_CLIENT:
-			purple_debug_warning("QQ", "QQ Server has newer client version\n");
-			break;
-		default:
-			qq_show_packet("Unknown sys msg", data, data_len);
-			purple_debug_warning("QQ", "Recv unknown sys msg code: %s\n", funct_str);
-			break;
-	}
-	g_strfreev(segments);
-}
-
-void qq_proc_server_cmd(PurpleConnection *gc, guint16 cmd, guint16 seq, guint8 *rcved, gint rcved_len)
-{
-	qq_data *qd;
-
-	guint8 *data;
-	gint data_len;
-
-	g_return_if_fail (gc != NULL && gc->proto_data != NULL);
-	qd = (qq_data *) gc->proto_data;
-
-	data = g_newa(guint8, rcved_len);
-	data_len = qq_decrypt(data, rcved, rcved_len, qd->session_key);
-	if (data_len < 0) {
-		purple_debug_warning("QQ",
-			"Can not decrypt server cmd by session key, [%05d], 0x%04X %s, len %d\n",
-			seq, cmd, qq_get_cmd_desc(cmd), rcved_len);
-		qq_show_packet("Can not decrypted", rcved, rcved_len);
-		return;
-	}
-
-	if (data_len <= 0) {
-		purple_debug_warning("QQ",
-			"Server cmd decrypted is empty, [%05d], 0x%04X %s, len %d\n",
-			seq, cmd, qq_get_cmd_desc(cmd), rcved_len);
-		return;
-	}
-
-	/* now process the packet */
-	switch (cmd) {
-		case QQ_CMD_RECV_IM:
-			process_private_msg(data, data_len, seq, gc);
-			break;
-		case QQ_CMD_RECV_MSG_SYS:
-			process_server_msg(gc, data, data_len, seq);
-			break;
-		case QQ_CMD_BUDDY_CHANGE_STATUS:
-			qq_process_buddy_change_status(data, data_len, gc);
-			break;
-		default:
-			process_unknow_cmd(gc, _("Unknown SERVER CMD"), data, data_len, cmd, seq);
-			break;
-	}
-}
-
-static void process_room_cmd_notify(PurpleConnection *gc,
-	guint8 room_cmd, guint8 room_id, guint8 reply, guint8 *data, gint data_len)
-{
-	gchar *prim;
-	gchar *msg, *msg_utf8;
-	g_return_if_fail(data != NULL && data_len > 0);
-
-	msg = g_strndup((gchar *) data, data_len);	/* it will append 0x00 */
-	msg_utf8 = qq_to_utf8(msg, QQ_CHARSET_DEFAULT);
-	g_free(msg);
-
-	prim = g_strdup_printf(_("Error reply of %s(0x%02X)\nRoom %u, reply 0x%02X"),
-		qq_get_room_cmd_desc(room_cmd), room_cmd, room_id, reply);
-
-	purple_notify_error(gc, _("QQ Qun Command"), prim, msg_utf8);
-
-	g_free(prim);
-	g_free(msg_utf8);
-}
-
-void qq_update_room(PurpleConnection *gc, guint8 room_cmd, guint32 room_id)
-{
-	qq_data *qd;
-	gint ret;
-
-	g_return_if_fail (gc != NULL && gc->proto_data != NULL);
-	qd = (qq_data *) gc->proto_data;
-
-	switch (room_cmd) {
-		case 0:
-			qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_INFO, room_id, NULL, 0,
-					QQ_CMD_CLASS_UPDATE_ROOM, 0);
-			break;
-		case QQ_ROOM_CMD_GET_INFO:
-			ret = qq_request_room_get_buddies(gc, room_id, QQ_CMD_CLASS_UPDATE_ROOM);
-			if (ret <= 0) {
-				qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_ONLINES, room_id, NULL, 0,
-						QQ_CMD_CLASS_UPDATE_ROOM, 0);
-			}
-			break;
-		case QQ_ROOM_CMD_GET_BUDDIES:
-			qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_ONLINES, room_id, NULL, 0,
-					QQ_CMD_CLASS_UPDATE_ROOM, 0);
-			break;
-		case QQ_ROOM_CMD_GET_ONLINES:
-			/* last command */
-		default:
-			break;
-	}
-}
-
-void qq_update_all_rooms(PurpleConnection *gc, guint8 room_cmd, guint32 room_id)
-{
-	qq_data *qd;
-	gboolean is_new_turn = FALSE;
-	guint32 next_id;
-
-	g_return_if_fail (gc != NULL && gc->proto_data != NULL);
-	qd = (qq_data *) gc->proto_data;
-
-	next_id = qq_room_get_next(gc, room_id);
-	purple_debug_info("QQ", "Update rooms, next id %u, prev id %u\n", next_id, room_id);
-
-	if (next_id <= 0) {
-		if (room_id > 0) {
-			is_new_turn = TRUE;
-			next_id = qq_room_get_next(gc, 0);
-			purple_debug_info("QQ", "New turn, id %u\n", next_id);
-		} else {
-			purple_debug_info("QQ", "No room. Finished update\n");
-			return;
-		}
-	}
-
-	switch (room_cmd) {
-		case 0:
-			qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_INFO, next_id, NULL, 0,
-					QQ_CMD_CLASS_UPDATE_ALL, 0);
-			break;
-		case QQ_ROOM_CMD_GET_INFO:
-			if (!is_new_turn) {
-				qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_INFO, next_id, NULL, 0,
-						QQ_CMD_CLASS_UPDATE_ALL, 0);
-			} else {
-				qq_request_room_get_buddies(gc, next_id, QQ_CMD_CLASS_UPDATE_ALL);
-			}
-			break;
-		case QQ_ROOM_CMD_GET_BUDDIES:
-			/* last command */
-			if (!is_new_turn) {
-				qq_request_room_get_buddies(gc, next_id, QQ_CMD_CLASS_UPDATE_ALL);
-			} else {
-				purple_debug_info("QQ", "Finished update\n");
-			}
-			break;
-		default:
-			break;
-	}
-}
-
-void qq_update_all(PurpleConnection *gc, guint16 cmd)
-{
-	qq_data *qd;
-
-	g_return_if_fail (gc != NULL && gc->proto_data != NULL);
-	qd = (qq_data *) gc->proto_data;
-
-	switch (cmd) {
-		case 0:
-			qq_request_buddy_info(gc, qd->uid, QQ_CMD_CLASS_UPDATE_ALL, 0);
-			break;
-		case QQ_CMD_GET_BUDDY_INFO:
-			qq_request_change_status(gc, QQ_CMD_CLASS_UPDATE_ALL);
-			break;
-		case QQ_CMD_CHANGE_STATUS:
-			qq_request_get_buddies(gc, 0, QQ_CMD_CLASS_UPDATE_ALL);
-			break;
-		case QQ_CMD_GET_BUDDIES_LIST:
-			qq_request_get_buddies_and_rooms(gc, 0, QQ_CMD_CLASS_UPDATE_ALL);
-			break;
-		case QQ_CMD_GET_BUDDIES_AND_ROOMS:
-			if (qd->client_version >= 2007) {
-				/* QQ2007/2008 can not get buddies level*/
-				qq_request_get_buddies_online(gc, 0, QQ_CMD_CLASS_UPDATE_ALL);
-			} else {
-				qq_request_get_buddies_level(gc, QQ_CMD_CLASS_UPDATE_ALL);
-			}
-			break;
-		case QQ_CMD_GET_LEVEL:
-			qq_request_get_buddies_online(gc, 0, QQ_CMD_CLASS_UPDATE_ALL);
-			break;
-		case QQ_CMD_GET_BUDDIES_ONLINE:
-			/* last command */
-			qq_update_all_rooms(gc, 0, 0);
-			break;
-		default:
-			break;
-	}
-	qd->online_last_update = time(NULL);
-}
-
-static void update_all_rooms_online(PurpleConnection *gc, guint8 room_cmd, guint32 room_id)
-{
-	qq_data *qd;
-	guint32 next_id;
-
-	g_return_if_fail (gc != NULL && gc->proto_data != NULL);
-	qd = (qq_data *) gc->proto_data;
-
-	next_id = qq_room_get_next_conv(gc, room_id);
-	if (next_id <= 0 && room_id <= 0) {
-		purple_debug_info("QQ", "No room in conversation, no update online buddies\n");
-		return;
-	}
-	if (next_id <= 0 ) {
-		purple_debug_info("QQ", "finished update rooms' online buddies\n");
-		return;
-	}
-
-	switch (room_cmd) {
-		case 0:
-			qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_ONLINES, next_id, NULL, 0,
-					QQ_CMD_CLASS_UPDATE_ALL, 0);
-			break;
-		case QQ_ROOM_CMD_GET_ONLINES:
-			qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_ONLINES, next_id, NULL, 0,
-					QQ_CMD_CLASS_UPDATE_ALL, 0);
-			break;
-		default:
-			break;
-	}
-}
-
-void qq_update_online(PurpleConnection *gc, guint16 cmd)
-{
-	qq_data *qd;
-	g_return_if_fail (gc != NULL && gc->proto_data != NULL);
-	qd = (qq_data *) gc->proto_data;
-
-	switch (cmd) {
-		case 0:
-			qq_request_get_buddies_online(gc, 0, QQ_CMD_CLASS_UPDATE_ONLINE);
-			break;
-		case QQ_CMD_GET_BUDDIES_ONLINE:
-			/* last command */
-			update_all_rooms_online(gc, 0, 0);
-			break;
-		default:
-			break;
-	}
-	qd->online_last_update = time(NULL);
-}
-
-void qq_proc_room_cmds(PurpleConnection *gc, guint16 seq,
-		guint8 room_cmd, guint32 room_id, guint8 *rcved, gint rcved_len,
-		guint32 update_class, guint32 ship32)
-{
-	qq_data *qd;
-	guint8 *data;
-	gint data_len;
-	qq_room_data *rmd;
-	gint bytes;
-	guint8 reply_cmd, reply;
-
-	g_return_if_fail (gc != NULL && gc->proto_data != NULL);
-	qd = (qq_data *) gc->proto_data;
-
-	data = g_newa(guint8, rcved_len);
-	data_len = qq_decrypt(data, rcved, rcved_len, qd->session_key);
-	if (data_len < 0) {
-		purple_debug_warning("QQ",
-			"Can not decrypt room cmd by session key, [%05d], 0x%02X %s for %d, len %d\n",
-			seq, room_cmd, qq_get_room_cmd_desc(room_cmd), room_id, rcved_len);
-		qq_show_packet("Can not decrypted", rcved, rcved_len);
-		return;
-	}
-
-	if (room_id <= 0) {
-		purple_debug_warning("QQ",
-			"Invaild room id, [%05d], 0x%02X %s for %d, len %d\n",
-			seq, room_cmd, qq_get_room_cmd_desc(room_cmd), room_id, rcved_len);
-		/* Some room cmd has no room id, like QQ_ROOM_CMD_SEARCH */
-	}
-
-	if (data_len <= 2) {
-		purple_debug_warning("QQ",
-			"Invaild len of room cmd decrypted, [%05d], 0x%02X %s for %d, len %d\n",
-			seq, room_cmd, qq_get_room_cmd_desc(room_cmd), room_id, rcved_len);
-		return;
-	}
-
-	bytes = 0;
-	bytes += qq_get8(&reply_cmd, data + bytes);
-	bytes += qq_get8(&reply, data + bytes);
-
-	if (reply_cmd != room_cmd) {
-		purple_debug_warning("QQ",
-			"Missing room cmd in reply 0x%02X %s, [%05d], 0x%02X %s for %d, len %d\n",
-			reply_cmd, qq_get_room_cmd_desc(reply_cmd),
-			seq, room_cmd, qq_get_room_cmd_desc(room_cmd), room_id, rcved_len);
-	}
-
-	/* now process the packet */
-	if (reply != QQ_ROOM_CMD_REPLY_OK) {
-		switch (reply) {	/* this should be all errors */
-		case QQ_ROOM_CMD_REPLY_NOT_MEMBER:
-			rmd = qq_room_data_find(gc, room_id);
-			if (rmd == NULL) {
-				purple_debug_warning("QQ",
-						"Missing room id in [%05d], 0x%02X %s for %d, len %d\n",
-						seq, room_cmd, qq_get_room_cmd_desc(room_cmd), room_id, rcved_len);
-			} else {
-				purple_debug_warning("QQ",
-					   "Not a member of room \"%s\"\n", rmd->title_utf8);
-				rmd->my_role = QQ_ROOM_ROLE_NO;
-			}
-			break;
-		case QQ_ROOM_CMD_REPLY_SEARCH_ERROR:
-			if (qd->roomlist != NULL) {
-				if (purple_roomlist_get_in_progress(qd->roomlist))
-					purple_roomlist_set_in_progress(qd->roomlist, FALSE);
-			}
-		default:
-			process_room_cmd_notify(gc, reply_cmd, room_id, reply, data + bytes, data_len - bytes);
-		}
-		return;
-	}
-
-	/* seems ok so far, so we process the reply according to sub_cmd */
-	switch (reply_cmd) {
-	case QQ_ROOM_CMD_GET_INFO:
-		qq_process_room_cmd_get_info(data + bytes, data_len - bytes, ship32, gc);
-		break;
-	case QQ_ROOM_CMD_CREATE:
-		qq_group_process_create_group_reply(data + bytes, data_len - bytes, gc);
-		break;
-	case QQ_ROOM_CMD_CHANGE_INFO:
-		qq_group_process_modify_info_reply(data + bytes, data_len - bytes, gc);
-		break;
-	case QQ_ROOM_CMD_MEMBER_OPT:
-		qq_group_process_modify_members_reply(data + bytes, data_len - bytes, gc);
-		break;
-	case QQ_ROOM_CMD_ACTIVATE:
-		qq_group_process_activate_group_reply(data + bytes, data_len - bytes, gc);
-		break;
-	case QQ_ROOM_CMD_SEARCH:
-		qq_process_room_search(gc, data + bytes, data_len - bytes, ship32);
-		break;
-	case QQ_ROOM_CMD_JOIN:
-		qq_process_group_cmd_join_group(data + bytes, data_len - bytes, gc);
-		break;
-	case QQ_ROOM_CMD_AUTH:
-		qq_process_group_cmd_join_group_auth(data + bytes, data_len - bytes, gc);
-		break;
-	case QQ_ROOM_CMD_QUIT:
-		qq_process_group_cmd_exit_group(data + bytes, data_len - bytes, gc);
-		break;
-	case QQ_ROOM_CMD_SEND_IM:
-		qq_process_room_send_im(gc, data + bytes, data_len - bytes);
-		break;
-	case QQ_ROOM_CMD_SEND_IM_EX:
-		qq_process_room_send_im_ex(gc, data + bytes, data_len - bytes);
-		break;
-	case QQ_ROOM_CMD_GET_ONLINES:
-		qq_process_room_cmd_get_onlines(data + bytes, data_len - bytes, gc);
-		break;
-	case QQ_ROOM_CMD_GET_BUDDIES:
-		qq_process_room_cmd_get_buddies(data + bytes, data_len - bytes, gc);
-		break;
-	default:
-		purple_debug_warning("QQ", "Unknow room cmd 0x%02X %s\n",
-			   reply_cmd, qq_get_room_cmd_desc(reply_cmd));
-	}
-
-	if (update_class == QQ_CMD_CLASS_NONE)
-		return;
-
-	if (update_class == QQ_CMD_CLASS_UPDATE_ALL) {
-		qq_update_all_rooms(gc, room_cmd, room_id);
-		return;
-	}
-	if (update_class == QQ_CMD_CLASS_UPDATE_ONLINE) {
-		update_all_rooms_online(gc, room_cmd, room_id);
-		return;
-	}
-	if (update_class == QQ_CMD_CLASS_UPDATE_ROOM) {
-		qq_update_room(gc, room_cmd, room_id);
-	}
-}
-
-guint8 qq_proc_login_cmds(PurpleConnection *gc,  guint16 cmd, guint16 seq,
-		guint8 *rcved, gint rcved_len, guint32 update_class, guint32 ship32)
-{
-	qq_data *qd;
-	guint8 *data = NULL;
-	gint data_len = 0;
-	guint ret_8 = QQ_LOGIN_REPLY_ERR;
-
-	g_return_val_if_fail (gc != NULL && gc->proto_data != NULL, QQ_LOGIN_REPLY_ERR);
-	qd = (qq_data *) gc->proto_data;
-
-	g_return_val_if_fail(rcved_len > 0, QQ_LOGIN_REPLY_ERR);
-	data = g_newa(guint8, rcved_len);
-
-	switch (cmd) {
-		case QQ_CMD_TOKEN:
-			if (qq_process_token(gc, rcved, rcved_len) == QQ_LOGIN_REPLY_OK) {
-				if (qd->client_version >= 2007) {
-					qq_request_token_ex(gc);
-				} else {
-					qq_request_login(gc);
-				}
-				return QQ_LOGIN_REPLY_OK;
-			}
-			return QQ_LOGIN_REPLY_ERR;
-		case QQ_CMD_GET_SERVER:
-		case QQ_CMD_TOKEN_EX:
-			data_len = qq_decrypt(data, rcved, rcved_len, qd->ld.random_key);
-			break;
-		case QQ_CMD_CHECK_PWD:
-			/* May use password_twice_md5 in the past version like QQ2005 */
-			data_len = qq_decrypt(data, rcved, rcved_len, qd->ld.random_key);
-			if (data_len >= 0) {
-				purple_debug_warning("QQ", "Decrypt login packet by random_key, %d bytes\n", data_len);
-			} else {
-				data_len = qq_decrypt(data, rcved, rcved_len, qd->ld.pwd_twice_md5);
-				if (data_len >= 0) {
-					purple_debug_warning("QQ", "Decrypt login packet by pwd_twice_md5, %d bytes\n", data_len);
-				}
-			}
-			break;
-		case QQ_CMD_LOGIN:
-		default:
-			if (qd->client_version >= 2007) {
-				data_len = qq_decrypt(data, rcved, rcved_len, qd->ld.pwd_twice_md5);
-				if (data_len >= 0) {
-					purple_debug_warning("QQ", "Decrypt login packet by pwd_twice_md5\n");
-				} else {
-					data_len = qq_decrypt(data, rcved, rcved_len, qd->ld.login_key);
-					if (data_len >= 0) {
-						purple_debug_warning("QQ", "Decrypt login packet by login_key\n");
-					}
-				}
-			} else {
-				/* May use password_twice_md5 in the past version like QQ2005 */
-				data_len = qq_decrypt(data, rcved, rcved_len, qd->ld.random_key);
-				if (data_len >= 0) {
-					purple_debug_warning("QQ", "Decrypt login packet by random_key\n");
-				} else {
-					data_len = qq_decrypt(data, rcved, rcved_len, qd->ld.pwd_twice_md5);
-					if (data_len >= 0) {
-						purple_debug_warning("QQ", "Decrypt login packet by pwd_twice_md5\n");
-					}
-				}
-			}
-			break;
-	}
-
-	if (data_len < 0) {
-		purple_debug_warning("QQ",
-				"Can not decrypt login cmd, [%05d], 0x%04X %s, len %d\n",
-				seq, cmd, qq_get_cmd_desc(cmd), rcved_len);
-		qq_show_packet("Can not decrypted", rcved, rcved_len);
-		purple_connection_error_reason(gc,
-				PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR,
-				_("Unable to decrypt login reply"));
-		return QQ_LOGIN_REPLY_ERR;
-	}
-
-	switch (cmd) {
-		case QQ_CMD_GET_SERVER:
-			ret_8 = qq_process_get_server(gc, data, data_len);
-			if ( ret_8 == QQ_LOGIN_REPLY_OK) {
-				qq_request_token(gc);
-			} else if ( ret_8 == QQ_LOGIN_REPLY_REDIRECT) {
-				return QQ_LOGIN_REPLY_REDIRECT;
-			}
-			break;
-		case QQ_CMD_TOKEN_EX:
-			ret_8 = qq_process_token_ex(gc, data, data_len);
-			if (ret_8 == QQ_LOGIN_REPLY_OK) {
-				qq_request_check_pwd(gc);
-			} else if (ret_8 == QQ_LOGIN_REPLY_NEXT_TOKEN_EX) {
-				qq_request_token_ex_next(gc);
-			} else if (ret_8 == QQ_LOGIN_REPLY_CAPTCHA_DLG) {
-				qq_captcha_input_dialog(gc, &(qd->captcha));
-				g_free(qd->captcha.token);
-				g_free(qd->captcha.data);
-				memset(&qd->captcha, 0, sizeof(qd->captcha));
-			}
-			break;
-		case QQ_CMD_CHECK_PWD:
-			ret_8 = qq_process_check_pwd(gc, data, data_len);
-			if (ret_8 != QQ_LOGIN_REPLY_OK) {
-				return ret_8;
-			}
-			if (qd->client_version >= 2008) {
-				qq_request_login_2008(gc);
-			} else {
-				qq_request_login_2007(gc);
-			}
-			break;
-		case QQ_CMD_LOGIN:
-			if (qd->client_version >= 2008) {
-				ret_8 = qq_process_login_2008(gc, data, data_len);
-				if ( ret_8 == QQ_LOGIN_REPLY_REDIRECT) {
-                		qq_request_get_server(gc);
-                		return QQ_LOGIN_REPLY_OK;
-            	}
-			} else if (qd->client_version >= 2007) {
-				ret_8 = qq_process_login_2007(gc, data, data_len);
-				if ( ret_8 == QQ_LOGIN_REPLY_REDIRECT) {
-                		qq_request_get_server(gc);
-                		return QQ_LOGIN_REPLY_OK;
-            	}
-			} else {
-				ret_8 = qq_process_login(gc, data, data_len);
-			}
-			if (ret_8 != QQ_LOGIN_REPLY_OK) {
-				return ret_8;
-			}
-
-			purple_connection_update_progress(gc, _("Logging in"), QQ_CONNECT_STEPS - 1, QQ_CONNECT_STEPS);
-			purple_debug_info("QQ", "Login replies OK; everything is fine\n");
-			purple_connection_set_state(gc, PURPLE_CONNECTED);
-			qd->is_login = TRUE;	/* must be defined after sev_finish_login */
-
-			/* now initiate QQ Qun, do it first as it may take longer to finish */
-			qq_room_data_initial(gc);
-
-			/* is_login, but we have packets before login */
-			qq_trans_process_remained(gc);
-
-			qq_update_all(gc, 0);
-			break;
-		default:
-			process_unknow_cmd(gc, _("Unknown LOGIN CMD"), data, data_len, cmd, seq);
-			return QQ_LOGIN_REPLY_ERR;
-	}
-	return QQ_LOGIN_REPLY_OK;
-}
-
-void qq_proc_client_cmds(PurpleConnection *gc, guint16 cmd, guint16 seq,
-		guint8 *rcved, gint rcved_len, guint32 update_class, guint32 ship32)
-{
-	qq_data *qd;
-
-	guint8 *data;
-	gint data_len;
-
-	guint8 ret_8 = 0;
-	guint16 ret_16 = 0;
-	guint32 ret_32 = 0;
-	gboolean is_unknow = FALSE;
-
-	g_return_if_fail(rcved_len > 0);
-
-	g_return_if_fail (gc != NULL && gc->proto_data != NULL);
-	qd = (qq_data *) gc->proto_data;
-
-	data = g_newa(guint8, rcved_len);
-	data_len = qq_decrypt(data, rcved, rcved_len, qd->session_key);
-	if (data_len < 0) {
-		purple_debug_warning("QQ",
-			"Reply can not be decrypted by session key, [%05d], 0x%04X %s, len %d\n",
-			seq, cmd, qq_get_cmd_desc(cmd), rcved_len);
-		qq_show_packet("Can not decrypted", rcved, rcved_len);
-		return;
-	}
-
-	if (data_len <= 0) {
-		purple_debug_warning("QQ",
-			"Reply decrypted is empty, [%05d], 0x%04X %s, len %d\n",
-			seq, cmd, qq_get_cmd_desc(cmd), rcved_len);
-		return;
-	}
-
-	switch (cmd) {
-		case QQ_CMD_UPDATE_INFO:
-			qq_process_change_info(gc, data, data_len);
-			break;
-		case QQ_CMD_ADD_BUDDY_NO_AUTH:
-			qq_process_add_buddy_no_auth(gc, data, data_len, ship32);
-			break;
-		case QQ_CMD_REMOVE_BUDDY:
-			qq_process_remove_buddy(gc, data, data_len, ship32);
-			break;
-		case QQ_CMD_REMOVE_ME:
-			qq_process_buddy_remove_me(gc, data, data_len, ship32);
-			break;
-		case QQ_CMD_ADD_BUDDY_AUTH:
-			qq_process_add_buddy_auth(data, data_len, gc);
-			break;
-		case QQ_CMD_GET_BUDDY_INFO:
-			qq_process_get_buddy_info(data, data_len, ship32, gc);
-			break;
-		case QQ_CMD_CHANGE_STATUS:
-			qq_process_change_status(data, data_len, gc);
-			break;
-		case QQ_CMD_SEND_IM:
-			do_im_ack(data, data_len, gc);
-			break;
-		case QQ_CMD_KEEP_ALIVE:
-			if (qd->client_version >= 2008) {
-				qq_process_keep_alive_2008(data, data_len, gc);
-			} else if (qd->client_version >= 2007) {
-				qq_process_keep_alive_2007(data, data_len, gc);
-			} else {
-				qq_process_keep_alive(data, data_len, gc);
-			}
-			break;
-		case QQ_CMD_GET_BUDDIES_ONLINE:
-			ret_8 = qq_process_get_buddies_online(data, data_len, gc);
-			if (ret_8  > 0 && ret_8 < 0xff) {
-				purple_debug_info("QQ", "Requesting for more online buddies\n");
-				qq_request_get_buddies_online(gc, ret_8, update_class);
-				return;
-			}
-			purple_debug_info("QQ", "All online buddies received\n");
-			qq_update_buddyies_status(gc);
-			break;
-		case QQ_CMD_GET_LEVEL:
-			qq_process_get_level_reply(data, data_len, gc);
-			break;
-		case QQ_CMD_GET_BUDDIES_LIST:
-			ret_16 = qq_process_get_buddies(data, data_len, gc);
-			if (ret_16 > 0	&& ret_16 < 0xffff) {
-				purple_debug_info("QQ", "Requesting for more buddies\n");
-				qq_request_get_buddies(gc, ret_16, update_class);
-				return;
-			}
-			purple_debug_info("QQ", "All buddies received. Requesting buddies' levels\n");
-			break;
-		case QQ_CMD_GET_BUDDIES_AND_ROOMS:
-			ret_32 = qq_process_get_buddies_and_rooms(data, data_len, gc);
-			if (ret_32 > 0 && ret_32 < 0xffffffff) {
-				purple_debug_info("QQ", "Requesting for more buddies and groups\n");
-				qq_request_get_buddies_and_rooms(gc, ret_32, update_class);
-				return;
-			}
-			purple_debug_info("QQ", "All buddies and groups received\n");
-			break;
-		case QQ_CMD_AUTH_CODE:
-			qq_process_auth_code(gc, data, data_len, ship32);
-			break;
-		case QQ_CMD_BUDDY_QUESTION:
-			qq_process_question(gc, data, data_len, ship32);
-			break;
-		case QQ_CMD_ADD_BUDDY_NO_AUTH_EX:
-			qq_process_add_buddy_no_auth_ex(gc, data, data_len, ship32);
-			break;
-		case QQ_CMD_ADD_BUDDY_AUTH_EX:
-			qq_process_add_buddy_auth_ex(gc, data, data_len, ship32);
-			break;
-		case QQ_CMD_BUDDY_CHECK_CODE:
-			qq_process_buddy_check_code(gc, data, data_len);
-			break;
-		case QQ_CMD_BUDDY_MEMO:
-			purple_debug_info("QQ", "Receive memo from server!\n");
-			qq_process_get_buddy_memo(gc, data, data_len, update_class, ship32);
-			return;
-			purple_debug_info("QQ", "Should NOT be here...\n");
-			break;
-		default:
-			process_unknow_cmd(gc, _("Unknown CLIENT CMD"), data, data_len, cmd, seq);
-			is_unknow = TRUE;
-			break;
-	}
-	if (is_unknow)
-		return;
-
-	if (update_class == QQ_CMD_CLASS_NONE)
-		return;
-
-	purple_debug_info("QQ", "Update class %d\n", update_class);
-	if (update_class == QQ_CMD_CLASS_UPDATE_ALL) {
-		qq_update_all(gc, cmd);
-		return;
-	}
-	if (update_class == QQ_CMD_CLASS_UPDATE_ONLINE) {
-		qq_update_online(gc, cmd);
-		return;
-	}
-}
-

mercurial