view libpurple/protocols/qq/send_core.c @ 23049:190bc4ecf6c3

patch-03-add-qq_hex_dump
author SHiNE CsyFeK <csyfek@gmail.com>
date Tue, 24 Jun 2008 12:09:16 +0000
parents 9a5d140400f1
children
line wrap: on
line source

/**
 * @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, PurpleConnection *gc, 
		guint16 cmd, gboolean is_auto_seq, guint16 *seq)
{
	qq_data *qd;
	gint bytes_expected, bytes;

	g_return_val_if_fail(buf != NULL, -1);

	qd = (qq_data *) gc->proto_data;
	if (is_auto_seq)
		*seq = ++(qd->send_seq);

	bytes = 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 += 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);

	if (bytes != bytes_expected) {
		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
				"Fail create qq header, expect %d bytes, written %d bytes\n", bytes_expected, bytes);
		bytes = -1;
	}
	return bytes;
}

/* 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 = 0;

	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",
					qq_get_cmd_desc(cmd), qd->send_seq, len);
			return -1;
		} else {	/* I update the len for TCP packet */
			/* set TCP packet length
			 * _create_packet_head_seq has reserved two byte for storing pkt length, ccpaging */
			qq_put16(buf, len);
		}
	}

	/* bytes actually returned */
	bytes = qq_proxy_write(qd, buf, len);

	if (bytes >= 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);
	}

	/* for debugging, s3e, 20070622 */
	_qq_show_packet("QQ_SEND_PACKET", p->buf, p->len);
	purple_debug(PURPLE_DEBUG_INFO, "QQ", "%d bytes written to the socket.\n", bytes);

	return bytes;
}

/* 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, *encrypted_data;
	guint16 seq_ret;
	gint encrypted_len, bytes, bytes_header, 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);

	qq_encrypt(data, len, qd->session_key, encrypted_data, &encrypted_len);

	seq_ret = seq;

	bytes = 0;
	bytes += _create_packet_head_seq(buf + bytes, gc, cmd, is_auto_seq, &seq_ret);
	if (bytes <= 0) {
		/* _create_packet_head_seq warned before */
		return -1;
	}
	
	bytes_header = bytes;
	bytes_expected = 4 + encrypted_len + 1;
	bytes += qq_put32(buf + bytes, (guint32) qd->uid);
	bytes += qq_putdata(buf + bytes, encrypted_data, encrypted_len);
	bytes += qq_put8(buf + bytes, QQ_PACKET_TAIL);

	if ((bytes - bytes_header) != bytes_expected) {	/* bad packet */
		purple_debug(PURPLE_DEBUG_ERROR, "QQ",
				"Fail creating packet, expect %d bytes, written %d bytes\n",
				bytes_expected, bytes - bytes_header);
		return -1;
	}

	/* 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, bytes, cmd);
	else		/* server's request, send ACK */
		bytes_sent = qq_proxy_write(qd, buf, bytes);

	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;
}