qulogic/pidgin

Add disconnection reasons to oscar.
cpw.resiak.disconnectreason
2007-10-01, Will Thompson
a9fc6198b5c6
Add disconnection reasons to oscar.
/**
* @file send_file.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 "qq.h"
#include "send_file.h"
#include "debug.h"
#include "network.h"
#include "notify.h"
#include "buddy_status.h"
#include "crypt.h"
#include "file_trans.h"
#include "header_info.h"
#include "im.h"
#include "keep_alive.h"
#include "packet_parse.h"
#include "send_core.h"
#include "utils.h"
enum
{
QQ_FILE_TRANS_REQ = 0x0035,
QQ_FILE_TRANS_ACC_UDP = 0x0037,
QQ_FILE_TRANS_ACC_TCP = 0x0003,
QQ_FILE_TRANS_DENY_UDP = 0x0039,
QQ_FILE_TRANS_DENY_TCP = 0x0005,
QQ_FILE_TRANS_NOTIFY = 0x003b,
QQ_FILE_TRANS_NOTIFY_ACK = 0x003c,
QQ_FILE_TRANS_CANCEL = 0x0049,
QQ_FILE_TRANS_PASV = 0x003f
};
static int _qq_in_same_lan(ft_info *info)
{
if (info->remote_internet_ip == info->local_internet_ip) return 1;
purple_debug(PURPLE_DEBUG_INFO, "QQ",
"Not in the same LAN, remote internet ip[%x], local internet ip[%x]\n",
info->remote_internet_ip
, info->local_internet_ip);
return 0;
}
static int _qq_xfer_init_udp_channel(ft_info *info)
{
struct sockaddr_in sin;
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
if (!_qq_in_same_lan(info)) {
sin.sin_port = g_htons(info->remote_major_port);
sin.sin_addr.s_addr = g_htonl(info->remote_internet_ip);
} else {
sin.sin_port = g_htons(info->remote_minor_port);
sin.sin_addr.s_addr = g_htonl(info->remote_real_ip);
}
return 0;
}
/* these 2 functions send and recv buffer from/to UDP channel */
static ssize_t _qq_xfer_udp_recv(guint8 *buf, size_t len, PurpleXfer *xfer)
{
struct sockaddr_in sin;
socklen_t sinlen;
ft_info *info;
gint r;
info = (ft_info *) xfer->data;
sinlen = sizeof(sin);
r = recvfrom(info->recv_fd, buf, len, 0, (struct sockaddr *) &sin, &sinlen);
purple_debug(PURPLE_DEBUG_INFO, "QQ",
"==> recv %d bytes from File UDP Channel, remote ip[%s], remote port[%d]\n",
r, inet_ntoa(sin.sin_addr), g_ntohs(sin.sin_port));
return r;
}
/*
static ssize_t _qq_xfer_udp_send(const char *buf, size_t len, PurpleXfer *xfer)
{
ft_info *info;
info = (ft_info *) xfer->data;
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;
ft_info *info;
info = (ft_info *) xfer->data;
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
if (!_qq_in_same_lan(info)) {
sin.sin_port = g_htons(info->remote_major_port);
sin.sin_addr.s_addr = g_htonl(info->remote_internet_ip);
} else if (info->use_major) {
sin.sin_port = g_htons(info->remote_major_port);
sin.sin_addr.s_addr = g_htonl(info->remote_real_ip);
} else {
sin.sin_port = g_htons(info->remote_minor_port);
sin.sin_addr.s_addr = g_htonl(info->remote_real_ip);
}
purple_debug(PURPLE_DEBUG_INFO, "QQ", "sending to channel: %d.%d.%d.%d:%d\n",
sin.sin_addr.s_addr & 0xff,
(sin.sin_addr.s_addr >> 8) & 0xff,
(sin.sin_addr.s_addr >> 16) & 0xff,
sin.sin_addr.s_addr >> 24,
g_ntohs(sin.sin_port)
);
return sendto(info->sender_fd, buf, len, 0, (struct sockaddr *) &sin, sizeof(sin));
}
/* user-defined functions for purple_xfer_read and purple_xfer_write */
/*
static ssize_t _qq_xfer_read(char **buf, PurpleXfer *xfer)
{
*buf = g_newa(char, QQ_FILE_FRAGMENT_MAXLEN + 100);
return _qq_xfer_udp_recv(*buf, QQ_FILE_FRAGMENT_MAXLEN + 100, xfer);
}
*/
gssize _qq_xfer_write(const guint8 *buf, size_t len, PurpleXfer *xfer)
{
return _qq_xfer_udp_send(buf, len, xfer);
}
static void _qq_xfer_recv_packet(gpointer data, gint source, PurpleInputCondition condition)
{
PurpleXfer *xfer = (PurpleXfer *) data;
PurpleAccount *account = purple_xfer_get_account(xfer);
PurpleConnection *gc = purple_account_get_connection(account);
guint8 *buf;
gint size;
/* FIXME: It seems that the transfer never use a packet
* larger than 1500 bytes, so if it happened to be a
* larger packet, either error occured or protocol should
* be modified
*/
ft_info *info;
info = xfer->data;
g_return_if_fail (source == info->recv_fd);
buf = g_newa(guint8, 1500);
size = _qq_xfer_udp_recv(buf, 1500, xfer);
qq_process_recv_file(gc, buf, size);
}
/* start file transfer process */
/*
static void _qq_xfer_send_start (PurpleXfer *xfer)
{
PurpleAccount *account;
PurpleConnection *gc;
ft_info *info;
account = purple_xfer_get_account(xfer);
gc = purple_account_get_connection(account);
info = (ft_info *) xfer->data;
}
*/
/*
static void _qq_xfer_send_ack (PurpleXfer *xfer, const char *buffer, size_t len)
{
PurpleAccount *account;
PurpleConnection *gc;
account = purple_xfer_get_account(xfer);
gc = purple_account_get_connection(account);
qq_process_recv_file(gc, (guint8 *) buffer, len);
}
*/
/*
static void _qq_xfer_recv_start(PurpleXfer *xfer)
{
}
*/
static void _qq_xfer_end(PurpleXfer *xfer)
{
ft_info *info;
g_return_if_fail(xfer != NULL && xfer->data != NULL);
info = (ft_info *) xfer->data;
qq_xfer_close_file(xfer);
if (info->dest_fp != NULL) {
fclose(info->dest_fp);
purple_debug(PURPLE_DEBUG_INFO, "QQ", "file closed\n");
}
if (info->major_fd != 0) {
close(info->major_fd);
purple_debug(PURPLE_DEBUG_INFO, "QQ", "major port closed\n");
}
if (info->minor_fd != 0) {
close(info->minor_fd);
purple_debug(PURPLE_DEBUG_INFO, "QQ", "minor port closed\n");
}
/*
if (info->buffer != NULL) {
munmap(info->buffer, purple_xfer_get_size(xfer));
purple_debug(PURPLE_DEBUG_INFO, "QQ", "file mapping buffer is freed.\n");
}
*/
g_free(info);
}
static void qq_show_conn_info(ft_info *info)
{
gchar *internet_ip_str, *real_ip_str;
guint32 ip;
ip = g_htonl(info->remote_real_ip);
real_ip_str = gen_ip_str((guint8 *) &ip);
ip = g_htonl(info->remote_internet_ip);
internet_ip_str = gen_ip_str((guint8 *) &ip);
purple_debug(PURPLE_DEBUG_INFO, "QQ", "remote internet ip[%s:%d], major port[%d], real ip[%s], minor port[%d]\n",
internet_ip_str, info->remote_internet_port,
info->remote_major_port, real_ip_str, info->remote_minor_port
);
g_free(real_ip_str);
g_free(internet_ip_str);
}
void qq_get_conn_info(guint8 *data, guint8 **cursor, gint data_len, ft_info *info)
{
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);
qq_show_conn_info(info);
}
gint qq_fill_conn_info(guint8 *raw_data, guint8 **cursor, ft_info *info)
{
gint bytes;
bytes = 0;
/* 064: connection method, UDP 0x00, TCP 0x03 */
bytes += create_packet_b (raw_data, cursor, info->conn_method);
/* 065-068: outer ip address of sender (proxy address) */
bytes += create_packet_dw (raw_data, cursor, info->local_internet_ip);
/* 069-070: sender port */
bytes += create_packet_w (raw_data, cursor, 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);
/* 073-076: real ip */
bytes += create_packet_dw (raw_data, cursor, info->local_real_ip);
/* 077-078: the second listening port */
bytes += create_packet_w (raw_data, cursor, 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)
{
gint bytes;
time_t now;
guint16 seq;
ft_info *info;
bytes = 0;
now = time(NULL);
if (!seq_ack) seq = qd->send_seq;
else {
info = (ft_info *) qd->xfer->data;
seq = info->send_seq;
}
/* 000-003: receiver uid */
bytes += create_packet_dw (raw_data, cursor, qd->uid);
/* 004-007: sender uid */
bytes += create_packet_dw (raw_data, cursor, to_uid);
/* 008-009: sender client version */
bytes += create_packet_w (raw_data, cursor, QQ_CLIENT);
/* 010-013: receiver uid */
bytes += create_packet_dw (raw_data, cursor, qd->uid);
/* 014-017: sender uid */
bytes += create_packet_dw (raw_data, cursor, to_uid);
/* 018-033: md5 of (uid+session_key) */
bytes += create_packet_data (raw_data, cursor, qd->session_md5, 16);
/* 034-035: message type */
bytes += create_packet_w (raw_data, cursor, message_type);
/* 036-037: sequence number */
bytes += create_packet_w (raw_data, cursor, seq);
/* 038-041: send time */
bytes += create_packet_dw (raw_data, cursor, (guint32) now);
/* 042-042: always 0x00 */
bytes += create_packet_b (raw_data, cursor, 0x00);
/* 043-043: sender icon */
bytes += create_packet_b (raw_data, cursor, qd->my_icon);
/* 044-046: always 0x00 */
bytes += create_packet_w (raw_data, cursor, 0x0000);
bytes += create_packet_b (raw_data, cursor, 0x00);
/* 047-047: we use font attr */
bytes += create_packet_b (raw_data, cursor, 0x01);
/* 048-051: always 0x00 */
bytes += create_packet_dw (raw_data, cursor, 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);
/* 063: transfer_type, 0x65: FILE 0x6b: FACE */
bytes += create_packet_b (raw_data, cursor, QQ_FILE_TRANSFER_FILE); /* FIXME */
return bytes;
}
#if 0
in_addr_t get_real_ip()
{
char hostname[40];
struct hostent *host;
gethostname(hostname, sizeof(hostname));
host = gethostbyname(hostname);
return *(host->h_addr);
}
#include <sys/ioctl.h>
#include <net/if.h>
#define MAXINTERFACES 16
in_addr_t get_real_ip()
{
int fd, intrface, i;
struct ifconf ifc;
struct ifreq buf[MAXINTERFACES];
in_addr_t ret;
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) return 0;
ifc.ifc_len = sizeof(buf);
ifc.ifc_buf = (caddr_t) buf;
if (ioctl(fd, SIOCGIFCONF, (char *) &ifc) < 0) return 0;
intrface = ifc.ifc_len / sizeof(struct ifreq);
for (i = 0; i < intrface; i++) {
/* buf[intrface].ifr_name */
if (ioctl(fd, SIOCGIFADDR, (char *) &buf[i]) >= 0)
{
ret = (((struct sockaddr_in *)(&buf[i].ifr_addr))->sin_addr).s_addr;
if (ret == g_ntohl(0x7f000001)) continue;
return ret;
}
}
return 0;
}
#endif
static void _qq_xfer_init_socket(PurpleXfer *xfer)
{
gint sockfd, listen_port = 0, i;
socklen_t sin_len;
struct sockaddr_in sin;
ft_info *info;
g_return_if_fail(xfer != NULL);
g_return_if_fail(xfer->data != NULL);
info = (ft_info *) xfer->data;
/* debug
info->local_real_ip = 0x7f000001;
*/
info->local_real_ip = g_ntohl(inet_addr(purple_network_get_my_ip(-1)));
purple_debug(PURPLE_DEBUG_INFO, "QQ", "local real ip is %x", info->local_real_ip);
for (i = 0; i < 2; i++) {
sockfd = socket(PF_INET, SOCK_DGRAM, 0);
g_return_if_fail(sockfd >= 0);
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = 0;
sin.sin_addr.s_addr = INADDR_ANY;
sin_len = sizeof(sin);
bind(sockfd, (struct sockaddr *) &sin, sin_len);
getsockname(sockfd, (struct sockaddr *) &sin, &sin_len);
listen_port = g_ntohs(sin.sin_port);
switch (i) {
case 0:
info->local_major_port = listen_port;
info->major_fd = sockfd;
purple_debug(PURPLE_DEBUG_INFO, "QQ", "UDP Major Channel created on port[%d]\n",
info->local_major_port);
break;
case 1:
info->local_minor_port = listen_port;
info->minor_fd = sockfd;
purple_debug(PURPLE_DEBUG_INFO, "QQ", "UDP Minor Channel created on port[%d]\n",
info->local_minor_port);
break;
}
}
if (_qq_in_same_lan(info)) {
info->sender_fd = info->recv_fd = info->minor_fd;
} else {
info->sender_fd = info->recv_fd = info->major_fd;
}
/* xfer->watcher = purple_input_add(info->recv_fd, PURPLE_INPUT_READ, _qq_xfer_recv_packet, xfer); */
}
/* create the QQ_FILE_TRANS_REQ packet with file infomations */
static void _qq_send_packet_file_request (PurpleConnection *gc, guint32 to_uid, gchar *filename, gint filesize)
{
qq_data *qd;
guint8 *cursor, *raw_data;
gchar *filelen_str;
gint filename_len, filelen_strlen, packet_len, bytes;
ft_info *info;
qd = (qq_data *) gc->proto_data;
info = g_new0(ft_info, 1);
info->to_uid = to_uid;
info->send_seq = qd->send_seq;
info->local_internet_ip = g_ntohl(inet_addr(qd->my_ip));
info->local_internet_port = qd->my_port;
info->local_real_ip = 0x00000000;
info->conn_method = 0x00;
qd->xfer->data = info;
filename_len = strlen(filename);
filelen_str = g_strdup_printf("%d ?Ö½?", filesize);
filelen_strlen = strlen(filelen_str);
packet_len = 82 + filename_len + filelen_strlen;
raw_data = g_newa(guint8, packet_len);
cursor = raw_data;
bytes = _qq_create_packet_file_header(raw_data, &cursor, to_uid,
QQ_FILE_TRANS_REQ, qd, FALSE);
bytes += qq_fill_conn_info(raw_data, &cursor, info);
/* 079: 0x20 */
bytes += create_packet_b (raw_data, &cursor, 0x20);
/* 080: 0x1f */
bytes += create_packet_b (raw_data, &cursor, 0x1f);
/* undetermined len: filename */
bytes += create_packet_data (raw_data, &cursor, (guint8 *) filename,
filename_len);
/* 0x1f */
bytes += create_packet_b (raw_data, &cursor, 0x1f);
/* file length */
bytes += create_packet_data (raw_data, &cursor, (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);
else
purple_debug (PURPLE_DEBUG_INFO, "qq_send_packet_file_request",
"%d bytes expected but got %d bytes\n",
packet_len, bytes);
g_free (filelen_str);
}
/* tell the buddy we want to accept the file */
static void _qq_send_packet_file_accept(PurpleConnection *gc, guint32 to_uid)
{
qq_data *qd;
guint8 *cursor, *raw_data;
guint16 minor_port;
guint32 real_ip;
gint packet_len, bytes;
ft_info *info;
qd = (qq_data *) gc->proto_data;
info = (ft_info *) qd->xfer->data;
purple_debug(PURPLE_DEBUG_INFO, "QQ", "I've accepted the file transfer request from %d\n", to_uid);
_qq_xfer_init_socket(qd->xfer);
packet_len = 79;
raw_data = g_newa (guint8, packet_len);
cursor = raw_data;
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);
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);
else
purple_debug (PURPLE_DEBUG_INFO, "qq_send_packet_file_accept",
"%d bytes expected but got %d bytes\n",
packet_len, bytes);
}
static void _qq_send_packet_file_notifyip(PurpleConnection *gc, guint32 to_uid)
{
PurpleXfer *xfer;
ft_info *info;
qq_data *qd;
guint8 *cursor, *raw_data;
gint packet_len, bytes;
qd = (qq_data *) gc->proto_data;
xfer = qd->xfer;
info = xfer->data;
packet_len = 79;
raw_data = g_newa (guint8, packet_len);
cursor = raw_data;
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);
if (packet_len == bytes)
qq_send_cmd (gc, QQ_CMD_SEND_IM, TRUE, 0, TRUE, raw_data,
cursor - raw_data);
else
purple_debug (PURPLE_DEBUG_INFO, "qq_send_packet_file_notify",
"%d bytes expected but got %d bytes\n",
packet_len, bytes);
if (xfer->watcher) purple_input_remove(xfer->watcher);
xfer->watcher = purple_input_add(info->recv_fd, PURPLE_INPUT_READ, _qq_xfer_recv_packet, xfer);
purple_input_add(info->major_fd, PURPLE_INPUT_READ, _qq_xfer_recv_packet, xfer);
}
/* tell the buddy we don't want the file */
static void _qq_send_packet_file_reject (PurpleConnection *gc, guint32 to_uid)
{
qq_data *qd;
guint8 *cursor, *raw_data;
gint packet_len, bytes;
purple_debug(PURPLE_DEBUG_INFO, "_qq_send_packet_file_reject", "start");
qd = (qq_data *) gc->proto_data;
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);
if (packet_len == bytes)
qq_send_cmd (gc, QQ_CMD_SEND_IM, TRUE, 0, TRUE, raw_data,
cursor - raw_data);
else
purple_debug (PURPLE_DEBUG_INFO, "qq_send_packet_file",
"%d bytes expected but got %d bytes\n",
packet_len, bytes);
}
/* tell the buddy to cancel transfer */
static void _qq_send_packet_file_cancel (PurpleConnection *gc, guint32 to_uid)
{
qq_data *qd;
guint8 *cursor, *raw_data;
gint packet_len, bytes;
purple_debug(PURPLE_DEBUG_INFO, "_qq_send_packet_file_cancel", "start\n");
qd = (qq_data *) gc->proto_data;
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);
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);
}
else
purple_debug (PURPLE_DEBUG_INFO, "qq_send_packet_file",
"%d bytes expected but got %d bytes\n",
packet_len, bytes);
purple_debug (PURPLE_DEBUG_INFO, "qq_send_packet_file_cancel", "end\n");
}
/* request to send a file */
static void
_qq_xfer_init (PurpleXfer * xfer)
{
PurpleConnection *gc;
PurpleAccount *account;
guint32 to_uid;
gchar *filename, *filename_without_path;
g_return_if_fail (xfer != NULL);
account = purple_xfer_get_account(xfer);
gc = purple_account_get_connection(account);
to_uid = purple_name_to_uid (xfer->who);
g_return_if_fail (to_uid != 0);
filename = (gchar *) purple_xfer_get_local_filename (xfer);
g_return_if_fail (filename != NULL);
filename_without_path = strrchr (filename, '/') + 1;
_qq_send_packet_file_request (gc, to_uid, filename_without_path,
purple_xfer_get_size(xfer));
}
/* cancel the transfer of receiving files */
static void _qq_xfer_cancel(PurpleXfer *xfer)
{
PurpleConnection *gc;
PurpleAccount *account;
guint16 *seq;
g_return_if_fail (xfer != NULL);
seq = (guint16 *) xfer->data;
account = purple_xfer_get_account(xfer);
gc = purple_account_get_connection(account);
switch (purple_xfer_get_status(xfer)) {
case PURPLE_XFER_STATUS_CANCEL_LOCAL:
_qq_send_packet_file_cancel(gc, purple_name_to_uid(xfer->who));
break;
case PURPLE_XFER_STATUS_CANCEL_REMOTE:
_qq_send_packet_file_cancel(gc, purple_name_to_uid(xfer->who));
break;
case PURPLE_XFER_STATUS_NOT_STARTED:
break;
case PURPLE_XFER_STATUS_UNKNOWN:
_qq_send_packet_file_reject(gc, purple_name_to_uid(xfer->who));
break;
case PURPLE_XFER_STATUS_DONE:
break;
case PURPLE_XFER_STATUS_ACCEPTED:
break;
case PURPLE_XFER_STATUS_STARTED:
break;
}
}
/* init the transfer of receiving files */
static void _qq_xfer_recv_init(PurpleXfer *xfer)
{
PurpleConnection *gc;
PurpleAccount *account;
ft_info *info;
g_return_if_fail (xfer != NULL && xfer->data != NULL);
info = (ft_info *) xfer->data;
account = purple_xfer_get_account(xfer);
gc = purple_account_get_connection(account);
_qq_send_packet_file_accept(gc, purple_name_to_uid(xfer->who));
}
/* process reject im for file transfer request */
void qq_process_recv_file_reject (guint8 *data, guint8 **cursor, gint data_len,
guint32 sender_uid, PurpleConnection *gc)
{
gchar *msg, *filename;
qq_data *qd;
g_return_if_fail (data != NULL && data_len != 0);
qd = (qq_data *) gc->proto_data;
g_return_if_fail (qd->xfer != NULL);
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);
purple_notify_warning (gc, _("File Send"), msg, NULL);
purple_xfer_request_denied(qd->xfer);
qd->xfer = NULL;
g_free (msg);
}
/* process cancel im for file transfer request */
void qq_process_recv_file_cancel (guint8 *data, guint8 **cursor, gint data_len,
guint32 sender_uid, PurpleConnection *gc)
{
gchar *msg, *filename;
qq_data *qd;
g_return_if_fail (data != NULL && data_len != 0);
qd = (qq_data *) gc->proto_data;
g_return_if_fail (qd->xfer != NULL
&& purple_xfer_get_filename(qd->xfer) != NULL);
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"),
sender_uid, filename);
purple_notify_warning (gc, _("File Send"), msg, NULL);
purple_xfer_cancel_remote(qd->xfer);
qd->xfer = NULL;
g_free (msg);
}
/* 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)
{
qq_data *qd;
ft_info *info;
PurpleXfer *xfer;
g_return_if_fail (data != NULL && data_len != 0);
qd = (qq_data *) gc->proto_data;
xfer = qd->xfer;
if (*cursor >= (data + data_len - 1)) {
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);
_qq_xfer_init_socket(qd->xfer);
_qq_xfer_init_udp_channel(info);
_qq_send_packet_file_notifyip(gc, sender_uid);
}
/* 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)
{
qq_data *qd;
PurpleXfer *xfer;
gchar *sender_name, **fileinfo;
ft_info *info;
PurpleBuddy *b;
qq_buddy *q_bud;
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->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));
*cursor = data + 18 + 12;
qq_get_conn_info(data, cursor, data_len, info);
fileinfo = g_strsplit((gchar *) (data + 81 + 12), "\x1f", 2);
g_return_if_fail (fileinfo != NULL && fileinfo[0] != NULL && fileinfo[1] != NULL);
sender_name = uid_to_purple_name(sender_uid);
/* FACE from IP detector, ignored by gfhuang */
if(g_ascii_strcasecmp(fileinfo[0], "FACE") == 0) {
purple_debug(PURPLE_DEBUG_WARNING, "QQ",
"Received a FACE ip detect from qq-%d, so he/she must be online :)\n", sender_uid);
b = purple_find_buddy(gc->account, sender_name);
q_bud = (b == NULL) ? NULL : (qq_buddy *) b->proto_data;
if (q_bud) {
if(0 != info->remote_real_ip) {
g_memmove(q_bud->ip, &info->remote_real_ip, 4);
q_bud->port = info->remote_minor_port;
}
else if (0 != info->remote_internet_ip) {
g_memmove(q_bud->ip, &info->remote_internet_ip, 4);
q_bud->port = info->remote_major_port;
}
if(!is_online(q_bud->status)) {
q_bud->status = QQ_BUDDY_ONLINE_INVISIBLE;
qq_update_buddy_contact(gc, q_bud);
}
else
purple_debug(PURPLE_DEBUG_INFO, "QQ", "buddy %d is already online\n", sender_uid);
}
else
purple_debug(PURPLE_DEBUG_WARNING, "QQ", "buddy %d is not in my friendlist\n", sender_uid);
g_free(sender_name);
g_strfreev(fileinfo);
return;
}
xfer = purple_xfer_new(purple_connection_get_account(gc),
PURPLE_XFER_RECEIVE,
sender_name);
if (xfer)
{
purple_xfer_set_filename(xfer, fileinfo[0]);
purple_xfer_set_size(xfer, atoi(fileinfo[1]));
purple_xfer_set_init_fnc(xfer, _qq_xfer_recv_init);
purple_xfer_set_request_denied_fnc(xfer, _qq_xfer_cancel);
purple_xfer_set_cancel_recv_fnc(xfer, _qq_xfer_cancel);
purple_xfer_set_end_fnc(xfer, _qq_xfer_end);
purple_xfer_set_write_fnc(xfer, _qq_xfer_write);
xfer->data = info;
qd->xfer = xfer;
purple_xfer_request(xfer);
}
g_free(sender_name);
g_strfreev(fileinfo);
}
static void _qq_xfer_send_notify_ip_ack(gpointer data, gint source, PurpleInputCondition cond)
{
PurpleXfer *xfer = (PurpleXfer *) data;
PurpleAccount *account = purple_xfer_get_account(xfer);
PurpleConnection *gc = purple_account_get_connection(account);
ft_info *info = (ft_info *) xfer->data;
purple_input_remove(xfer->watcher);
xfer->watcher = purple_input_add(info->recv_fd, PURPLE_INPUT_READ, _qq_xfer_recv_packet, xfer);
qq_send_file_ctl_packet(gc, QQ_FILE_CMD_NOTIFY_IP_ACK, info->to_uid, 0);
/*
info->use_major = TRUE;
qq_send_file_ctl_packet(gc, QQ_FILE_CMD_NOTIFY_IP_ACK, info->to_uid, 0);
info->use_major = FALSE;
*/
}
void qq_process_recv_file_notify(guint8 *data, guint8 **cursor, gint data_len,
guint32 sender_uid, PurpleConnection *gc)
{
qq_data *qd;
ft_info *info;
PurpleXfer *xfer;
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 notify message is empty\n");
return;
}
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);
_qq_xfer_init_udp_channel(info);
xfer->watcher = purple_input_add(info->sender_fd, PURPLE_INPUT_WRITE, _qq_xfer_send_notify_ip_ack, xfer);
}
/* temp placeholder until a working function can be implemented */
gboolean qq_can_receive_file(PurpleConnection *gc, const char *who)
{
return TRUE;
}
void qq_send_file(PurpleConnection *gc, const char *who, const char *file)
{
qq_data *qd;
PurpleXfer *xfer;
qd = (qq_data *) gc->proto_data;
xfer = purple_xfer_new (gc->account, PURPLE_XFER_SEND,
who);
if (xfer)
{
purple_xfer_set_init_fnc (xfer, _qq_xfer_init);
purple_xfer_set_cancel_send_fnc (xfer, _qq_xfer_cancel);
purple_xfer_set_write_fnc(xfer, _qq_xfer_write);
qd->xfer = xfer;
purple_xfer_request(xfer);
}
}
/*
static void qq_send_packet_request_key(PurpleConnection *gc, guint8 key)
{
qq_send_cmd(gc, QQ_CMD_REQUEST_KEY, TRUE, 0, TRUE, &key, 1);
}
static void qq_process_recv_request_key(PurpleConnection *gc)
{
}
*/