Mercurial > pidgin
view src/protocols/qq/send_file.c @ 13967:99b9b58b19dd
[gaim-migrate @ 16523]
Fix a crazy MSN crash. Basically it's possible to have more than one
slplink associated with a given switchboard, but our code did not
allow for that. I think it happens when you're in a multi-user
chat and you do stuff with multiple users that involves slplinks.
Like maybe file transfer and buddy icon related stuff.
Tracking this down took an ungodly amount of time, but thanks to
Meebo for letting me do it :-)
committer: Tailor Script <tailor@pidgin.im>
author | Mark Doliner <mark@kingant.net> |
---|---|
date | Thu, 20 Jul 2006 07:31:15 +0000 |
parents | 2be9dfa9569b |
children | ef8490f9e823 |
line wrap: on
line source
/** * The QQ2003C protocol plugin * * for gaim * * Author: Henry Ou <henry@linux.net> * * 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 <network.h>, beta2, gfhuang #include "send_file.h" #include "debug.h" #include "notify.h" #include "network.h" //gaim_network_get_my_ip #include "im.h" //qq_create_packet_im_header #include "packet_parse.h" #include "crypt.h" #include "header_info.h" #include "send_core.h" #include "utils.h" // gaim_name_to_uid #include "file_trans.h" // qq_send_file_ctl #include "qq.h" #include "buddy_status.h" // by gfhuang #include "keep_alive.h" //by gfhuang 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; gaim_debug(GAIM_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 = htons(info->remote_major_port); sin.sin_addr.s_addr = htonl(info->remote_internet_ip); } else { sin.sin_port = htons(info->remote_minor_port); sin.sin_addr.s_addr = htonl(info->remote_real_ip); } return 0; // return connect(info->sender_fd, (struct sockaddr *) &sin, sizeof(sin)); } /* these 2 functions send and recv buffer from/to UDP channel */ static ssize_t _qq_xfer_udp_recv(char *buf, size_t len, GaimXfer *xfer) { struct sockaddr_in sin; int sinlen; ft_info *info; int r; info = (ft_info *) xfer->data; sinlen = sizeof(sin); r = recvfrom(info->recv_fd, buf, len, 0, (struct sockaddr *) &sin, &sinlen); gaim_debug(GAIM_DEBUG_INFO, "QQ", "==> recv %d bytes from File UDP Channel, remote ip[%s], remote port[%d]\n", r, inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); return r; } /* static ssize_t _qq_xfer_udp_send(const char *buf, size_t len, GaimXfer *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 char *buf, size_t len, GaimXfer *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 = htons(info->remote_major_port); sin.sin_addr.s_addr = htonl(info->remote_internet_ip); } else if (info->use_major) { sin.sin_port = htons(info->remote_major_port); sin.sin_addr.s_addr = htonl(info->remote_real_ip); } else { sin.sin_port = htons(info->remote_minor_port); sin.sin_addr.s_addr = htonl(info->remote_real_ip); } gaim_debug(GAIM_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, ntohs(sin.sin_port) ); return sendto(info->sender_fd, buf, len, 0, (struct sockaddr *) &sin, sizeof(sin)); } /* user-defined functions for gaim_xfer_read and gaim_xfer_write */ /* static ssize_t _qq_xfer_read(char **buf, GaimXfer *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 guchar *buf, size_t len, GaimXfer *xfer) //gfhuang { return _qq_xfer_udp_send(buf, len, xfer); } static void _qq_xfer_recv_packet(gpointer data, gint source, GaimInputCondition condition) { GaimXfer *xfer = (GaimXfer *) data; GaimAccount *account = gaim_xfer_get_account(xfer); GaimConnection *gc = gaim_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 (GaimXfer * xfer) { GaimAccount *account; GaimConnection *gc; ft_info *info; account = gaim_xfer_get_account(xfer); gc = gaim_account_get_connection(account); info = (ft_info *) xfer->data; } static void _qq_xfer_send_ack (GaimXfer *xfer, const char *buffer, size_t len) { GaimAccount *account; GaimConnection *gc; account = gaim_xfer_get_account(xfer); gc = gaim_account_get_connection(account); qq_process_recv_file(gc, (guint8 *) buffer, len); } static void _qq_xfer_recv_start(GaimXfer *xfer) { } static void _qq_xfer_end(GaimXfer *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); gaim_debug(GAIM_DEBUG_INFO, "QQ", "file closed\n"); } if (info->major_fd != 0) { close(info->major_fd); gaim_debug(GAIM_DEBUG_INFO, "QQ", "major port closed\n"); } if (info->minor_fd != 0) { close(info->minor_fd); gaim_debug(GAIM_DEBUG_INFO, "QQ", "minor port closed\n"); } // if (info->buffer != NULL) { // munmap(info->buffer, gaim_xfer_get_size(xfer)); // gaim_debug(GAIM_DEBUG_INFO, "QQ", "file mapping buffer is freed.\n"); // } g_free(info); } void qq_show_conn_info(ft_info *info) { gchar *internet_ip_str, *real_ip_str; guint32 ip; ip = htonl(info->remote_real_ip); real_ip_str = gen_ip_str((guint8 *) &ip); ip = htonl(info->remote_internet_ip); internet_ip_str = gen_ip_str((guint8 *) &ip); gaim_debug(GAIM_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; } extern gchar *_gen_session_md5(gint uid, gchar *session_key); /*****************************************************************************/ // 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; gchar *md5; guint16 seq; ft_info *info; bytes = 0; now = time(NULL); md5 = _gen_session_md5(qd->uid, qd->session_key); 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, 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 by gfhuang */ g_free (md5); return bytes; } //_qq_create_packet_file_header #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 == ntohl(0x7f000001)) continue; return ret; } } return 0; } #endif static void _qq_xfer_init_socket(GaimXfer *xfer) { int sockfd, listen_port = 0, i, 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; // info->local_real_ip = ntohl(get_real_ip()); //debug // info->local_real_ip = 0x7f000001; info->local_real_ip = ntohl(inet_addr(gaim_network_get_my_ip(-1))); gaim_debug(GAIM_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 = ntohs(sin.sin_port); switch (i) { case 0: info->local_major_port = listen_port; info->major_fd = sockfd; gaim_debug(GAIM_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; gaim_debug(GAIM_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 = gaim_input_add(info->recv_fd, GAIM_INPUT_READ, _qq_xfer_recv_packet, xfer); } /*****************************************************************************/ // create the QQ_FILE_TRANS_REQ packet with file infomations static void _qq_send_packet_file_request (GaimConnection * 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 = 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, filename, filename_len); // 0x1f bytes += create_packet_b (raw_data, &cursor, 0x1f); // file length bytes += create_packet_data (raw_data, &cursor, 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 gaim_debug (GAIM_DEBUG_INFO, "qq_send_packet_file_request", "%d bytes expected but got %d bytes\n", packet_len, bytes); g_free (filelen_str); } //_qq_send_packet_file_request /*****************************************************************************/ // tell the buddy we want to accept the file static void _qq_send_packet_file_accept(GaimConnection *gc, guint32 to_uid) { qq_data *qd; guint8 *cursor, *raw_data; gint packet_len, bytes; ft_info *info; g_return_if_fail (gc != NULL && gc->proto_data != NULL); qd = (qq_data *) gc->proto_data; info = (ft_info *) qd->xfer->data; gaim_debug(GAIM_DEBUG_INFO, "QQ", "I've accepted the file transfer request from %d\n", to_uid); _qq_xfer_init_socket(qd->xfer); guint16 minor_port; guint32 real_ip; 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 gaim_debug (GAIM_DEBUG_INFO, "qq_send_packet_file_accept", "%d bytes expected but got %d bytes\n", packet_len, bytes); } //_qq_send_packet_packet_accept static void _qq_send_packet_file_notifyip(GaimConnection *gc, guint32 to_uid) { GaimXfer *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; gaim_debug(GAIM_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 gaim_debug (GAIM_DEBUG_INFO, "qq_send_packet_file_notify", "%d bytes expected but got %d bytes\n", packet_len, bytes); if (xfer->watcher) gaim_input_remove(xfer->watcher); xfer->watcher = gaim_input_add(info->recv_fd, GAIM_INPUT_READ, _qq_xfer_recv_packet, xfer); gaim_input_add(info->major_fd, GAIM_INPUT_READ, _qq_xfer_recv_packet, xfer); // gaim_input_add(info->minor_fd, GAIM_INPUT_READ, _qq_xfer_recv_packet, xfer); } /*****************************************************************************/ // tell the buddy we don't want the file static void _qq_send_packet_file_reject (GaimConnection *gc, guint32 to_uid) { qq_data *qd; guint8 *cursor, *raw_data; gint packet_len, bytes; gaim_debug(GAIM_DEBUG_INFO, "_qq_send_packet_file_reject", "start"); g_return_if_fail(gc != NULL && gc->proto_data != NULL); 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 gaim_debug (GAIM_DEBUG_INFO, "qq_send_packet_file", "%d bytes expected but got %d bytes\n", packet_len, bytes); // gaim_debug (GAIM_DEBUG_INFO, "qq_send_packet_file_reject", "end\n"); } // qq_send_packet_file_reject /*****************************************************************************/ // tell the buddy to cancel transfer static void _qq_send_packet_file_cancel (GaimConnection *gc, guint32 to_uid) { qq_data *qd; guint8 *cursor, *raw_data; gint packet_len, bytes; gaim_debug(GAIM_DEBUG_INFO, "_qq_send_packet_file_cancel", "start\n"); g_return_if_fail(gc != NULL && gc->proto_data != NULL); qd = (qq_data *) gc->proto_data; packet_len = 64; raw_data = g_newa (guint8, packet_len); cursor = raw_data; bytes = 0; gaim_debug(GAIM_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); gaim_debug(GAIM_DEBUG_INFO, "_qq_send_packet_file_cancel", "end create header\n"); if (packet_len == bytes) { gaim_debug(GAIM_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 gaim_debug (GAIM_DEBUG_INFO, "qq_send_packet_file", "%d bytes expected but got %d bytes\n", packet_len, bytes); gaim_debug (GAIM_DEBUG_INFO, "qq_send_packet_file_cancel", "end\n"); } // qq_send_packet_file_cancel /*****************************************************************************/ // request to send a file static void _qq_xfer_init (GaimXfer * xfer) { GaimConnection *gc; GaimAccount *account; guint32 to_uid; gchar *filename, *filename_without_path; g_return_if_fail (xfer != NULL); account = gaim_xfer_get_account(xfer); gc = gaim_account_get_connection(account); g_return_if_fail (gc != NULL && gc->proto_data != NULL); to_uid = gaim_name_to_uid (xfer->who); g_return_if_fail (to_uid != 0); filename = (gchar *) gaim_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, gaim_xfer_get_size(xfer)); } // qq_xfer_init /*****************************************************************************/ // cancel the transfer of receiving files static void _qq_xfer_cancel(GaimXfer *xfer) { GaimConnection *gc; GaimAccount *account; guint16 *seq; g_return_if_fail (xfer != NULL); seq = (guint16 *) xfer->data; account = gaim_xfer_get_account(xfer); gc = gaim_account_get_connection(account); switch (gaim_xfer_get_status(xfer)) { case GAIM_XFER_STATUS_CANCEL_LOCAL: _qq_send_packet_file_cancel(gc, gaim_name_to_uid(xfer->who)); break; case GAIM_XFER_STATUS_CANCEL_REMOTE: _qq_send_packet_file_cancel(gc, gaim_name_to_uid(xfer->who)); break; case GAIM_XFER_STATUS_NOT_STARTED: break; case GAIM_XFER_STATUS_UNKNOWN: _qq_send_packet_file_reject(gc, gaim_name_to_uid(xfer->who)); break; case GAIM_XFER_STATUS_DONE: break; case GAIM_XFER_STATUS_ACCEPTED: break; case GAIM_XFER_STATUS_STARTED: break; } } /*****************************************************************************/ // init the transfer of receiving files static void _qq_xfer_recv_init(GaimXfer *xfer) { GaimConnection *gc; GaimAccount *account; ft_info *info; g_return_if_fail (xfer != NULL && xfer->data != NULL); info = (ft_info *) xfer->data; account = gaim_xfer_get_account(xfer); gc = gaim_account_get_connection(account); _qq_send_packet_file_accept(gc, gaim_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, GaimConnection * gc) { gchar *msg, *filename; qq_data *qd; g_return_if_fail (gc != NULL && data != NULL && data_len != 0); qd = (qq_data *) gc->proto_data; g_return_if_fail (qd != NULL && qd->xfer != NULL); if (*cursor >= (data + data_len - 1)) { gaim_debug (GAIM_DEBUG_WARNING, "QQ", "Received file reject message is empty\n"); return; } filename = strrchr(gaim_xfer_get_local_filename(qd->xfer), '/') + 1; msg = g_strdup_printf (_ ("Your request to send file[%s] has been rejected by buddy[%d]"), filename, sender_uid); gaim_notify_warning (gc, _("File Send"), msg, NULL); gaim_xfer_request_denied(qd->xfer); qd->xfer = NULL; g_free (msg); } // qq_process_recv_file_reject /*****************************************************************************/ // process cancel im for file transfer request void qq_process_recv_file_cancel (guint8 * data, guint8 ** cursor, gint data_len, guint32 sender_uid, GaimConnection * gc) { gchar *msg, *filename; qq_data *qd; g_return_if_fail (gc != NULL && data != NULL && data_len != 0); qd = (qq_data *) gc->proto_data; g_return_if_fail (qd != NULL && qd->xfer != NULL && gaim_xfer_get_filename(qd->xfer) != NULL); if (*cursor >= (data + data_len - 1)) { gaim_debug (GAIM_DEBUG_WARNING, "QQ", "Received file reject message is empty\n"); return; } filename = strrchr(gaim_xfer_get_local_filename(qd->xfer), '/') + 1; msg = g_strdup_printf (_("The sending process of file[%s] has been cancaled by buddy[%d]"), filename, sender_uid); gaim_notify_warning (gc, _("File Send"), msg, NULL); gaim_xfer_cancel_remote(qd->xfer); qd->xfer = NULL; g_free (msg); } // qq_process_recv_file_cancel /*****************************************************************************/ // process accept im for file transfer request void qq_process_recv_file_accept (guint8 * data, guint8 ** cursor, gint data_len, guint32 sender_uid, GaimConnection * gc) { qq_data *qd; ft_info *info; GaimXfer *xfer; g_return_if_fail (gc != NULL && data != NULL && data_len != 0); qd = (qq_data *) gc->proto_data; xfer = qd->xfer; if (*cursor >= (data + data_len - 1)) { gaim_debug (GAIM_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); } // qq_process_recv_file_accept /*****************************************************************************/ // 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, GaimConnection * gc) { qq_data *qd; GaimXfer *xfer; gchar *sender_name; ft_info *info; GaimBuddy *b; //by gfhuang qq_buddy *q_bud; g_return_if_fail (gc != NULL && data != NULL && data_len != 0); qd = (qq_data *) gc->proto_data; if (*cursor >= (data + data_len - 1)) { gaim_debug (GAIM_DEBUG_WARNING, "QQ", "Received file reject message is empty\n"); return; } info = g_new0(ft_info, 1); info->local_internet_ip = 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); gchar **fileinfo; fileinfo = g_strsplit(data + 81 + 12, "\x1f", 2); g_return_if_fail (fileinfo != NULL && fileinfo[0] != NULL && fileinfo[1] != NULL); sender_name = uid_to_gaim_name(sender_uid); //FACE from IP detector, ignored by gfhuang if(g_ascii_strcasecmp(fileinfo[0], "FACE") == 0) { gaim_debug(GAIM_DEBUG_WARNING, "QQ", "Received a FACE ip detect from qq-%d, so he/she must be online :)\n", sender_uid); b = gaim_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) { //by gfhuang 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 gaim_debug(GAIM_DEBUG_INFO, "QQ", "buddy %d is already online\n", sender_uid); } else gaim_debug(GAIM_DEBUG_WARNING, "QQ", "buddy %d is not in my friendlist\n", sender_uid); g_free(sender_name); g_strfreev(fileinfo); return; } xfer = gaim_xfer_new(gaim_connection_get_account(gc), GAIM_XFER_RECEIVE, sender_name); gaim_xfer_set_filename(xfer, fileinfo[0]); gaim_xfer_set_size(xfer, atoi(fileinfo[1])); gaim_xfer_set_init_fnc(xfer, _qq_xfer_recv_init); gaim_xfer_set_request_denied_fnc(xfer, _qq_xfer_cancel); gaim_xfer_set_cancel_recv_fnc(xfer, _qq_xfer_cancel); gaim_xfer_set_end_fnc(xfer, _qq_xfer_end); gaim_xfer_set_write_fnc(xfer, _qq_xfer_write); xfer->data = info; qd->xfer = xfer; gaim_xfer_request(xfer); g_free(sender_name); g_strfreev(fileinfo); // gaim_debug (GAIM_DEBUG_INFO, "qq_process_recv_file_request", "end\n"); } // qq_process_recv_file_request static void _qq_xfer_send_notify_ip_ack(gpointer data, gint source, GaimInputCondition cond) { GaimXfer *xfer = (GaimXfer *) data; GaimAccount *account = gaim_xfer_get_account(xfer); GaimConnection *gc = gaim_account_get_connection(account); ft_info *info = (ft_info *) xfer->data; gaim_input_remove(xfer->watcher); xfer->watcher = gaim_input_add(info->recv_fd, GAIM_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, GaimConnection * gc) { qq_data *qd; ft_info *info; GaimXfer *xfer; g_return_if_fail (gc != NULL && data != NULL && data_len != 0); qd = (qq_data *) gc->proto_data; if (*cursor >= (data + data_len - 1)) { gaim_debug (GAIM_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 = gaim_input_add(info->sender_fd, GAIM_INPUT_WRITE, _qq_xfer_send_notify_ip_ack, xfer); } //temp placeholder until a working function can be implemented gboolean qq_can_receive_file(GaimConnection *gc, const char *who) { return TRUE; } void qq_send_file(GaimConnection *gc, const char *who, const char *file) { qq_data *qd; GaimXfer *xfer; g_return_if_fail (gc != NULL && gc->proto_data != NULL); qd = (qq_data *) gc->proto_data; xfer = gaim_xfer_new (gc->account, GAIM_XFER_SEND, who); gaim_xfer_set_init_fnc (xfer, _qq_xfer_init); gaim_xfer_set_cancel_send_fnc (xfer, _qq_xfer_cancel); gaim_xfer_set_write_fnc(xfer, _qq_xfer_write); qd->xfer = xfer; gaim_xfer_request (xfer); } static void qq_send_packet_request_key(GaimConnection *gc, guint8 key) { qq_send_cmd(gc, QQ_CMD_REQUEST_KEY, TRUE, 0, TRUE, &key, 1); } static void qq_process_recv_request_key(GaimConnection *gc) { }