diff libgaim/protocols/qq/recv_core.c @ 14192:60b1bc8dbf37

[gaim-migrate @ 16863] Renamed 'core' to 'libgaim' committer: Tailor Script <tailor@pidgin.im>
author Evan Schoenberg <evan.s@dreskin.net>
date Sat, 19 Aug 2006 01:50:10 +0000
parents
children 902c3aa4950a
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libgaim/protocols/qq/recv_core.c	Sat Aug 19 01:50:10 2006 +0000
@@ -0,0 +1,312 @@
+/**
+* The QQ2003C protocol plugin
+ *
+ * for gaim
+ *
+ * Copyright (C) 2004 Puzzlebird
+ *
+ * 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 "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, GaimConnection *gc)
+{
+	qq_data *qd;
+	guint8 *byte, mask;
+
+	g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, FALSE);
+	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 */
+}				/* _qq_check_packet_set_window */
+
+/* default process, decrypt and dump */
+static void _qq_process_packet_default(guint8 *buf, gint buf_len, guint16 cmd, guint16 seq, GaimConnection *gc)
+{
+	qq_data *qd;
+	guint8 *data;
+	gchar *msg_utf8;
+	gint len;
+
+	g_return_if_fail(gc != NULL && gc->proto_data != NULL);
+	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_crypt(DECRYPT, buf, buf_len, qd->session_key, data, &len)) {
+		gaim_debug(GAIM_DEBUG_WARNING, "QQ",
+			   ">>> [%d] %s, %d bytes -> [default] decrypt and dump\n%s",
+			   seq, qq_get_cmd_desc(cmd), buf_len, hex_dump_to_str(data, len));
+		try_dump_as_gbk(data, len);
+	} else {
+		gaim_debug(GAIM_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, GaimConnection *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(gc != NULL && gc->proto_data != NULL);
+	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) {
+		gaim_debug(GAIM_DEBUG_ERROR,
+			   "QQ", "Received packet is too short, dump and drop\n%s", hex_dump_to_str(buf, buf_len));
+		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 */
+			gaim_debug
+			    (GAIM_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 */
+		gaim_debug(GAIM_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)) {
+		gaim_debug(GAIM_DEBUG_ERROR,
+			   "QQ", "Unknown QQ proctocol, dump and drop\n%s", hex_dump_to_str(buf, buf_len));
+		return;
+	}
+
+	if (QQ_DEBUG)
+		gaim_debug(GAIM_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)) {
+			gaim_debug(GAIM_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)
+				gaim_debug(GAIM_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_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, GaimInputCondition cond)
+{
+	GaimConnection *gc;
+	qq_data *qd;
+	guint8 *buf;
+	gint len;
+
+	gc = (GaimConnection *) data;
+	g_return_if_fail(gc != NULL && gc->proto_data != NULL && cond == GAIM_INPUT_READ);
+
+	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) {
+		gaim_connection_error(gc, _("Unable to read from socket"));
+		return;
+	} else {
+		_qq_packet_process(buf, len, gc);
+	}
+}