| |
1 /** |
| |
2 * @file send_file.c |
| |
3 * |
| |
4 * purple |
| |
5 * |
| |
6 * Purple is the legal property of its developers, whose names are too numerous |
| |
7 * to list here. Please refer to the COPYRIGHT file distributed with this |
| |
8 * source distribution. |
| |
9 * |
| |
10 * This program is free software; you can redistribute it and/or modify |
| |
11 * it under the terms of the GNU General Public License as published by |
| |
12 * the Free Software Foundation; either version 2 of the License, or |
| |
13 * (at your option) any later version. |
| |
14 * |
| |
15 * This program is distributed in the hope that it will be useful, |
| |
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| |
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| |
18 * GNU General Public License for more details. |
| |
19 * |
| |
20 * You should have received a copy of the GNU General Public License |
| |
21 * along with this program; if not, write to the Free Software |
| |
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| |
23 */ |
| |
24 |
| |
25 #include "send_file.h" |
| |
26 #include "debug.h" |
| |
27 #include "network.h" |
| |
28 #include "notify.h" |
| |
29 |
| |
30 #include "buddy_status.h" |
| |
31 #include "crypt.h" |
| |
32 #include "file_trans.h" |
| |
33 #include "header_info.h" |
| |
34 #include "im.h" |
| |
35 #include "keep_alive.h" |
| |
36 #include "packet_parse.h" |
| |
37 #include "qq.h" |
| |
38 #include "send_core.h" |
| |
39 #include "utils.h" |
| |
40 |
| |
41 enum |
| |
42 { |
| |
43 QQ_FILE_TRANS_REQ = 0x0035, |
| |
44 QQ_FILE_TRANS_ACC_UDP = 0x0037, |
| |
45 QQ_FILE_TRANS_ACC_TCP = 0x0003, |
| |
46 QQ_FILE_TRANS_DENY_UDP = 0x0039, |
| |
47 QQ_FILE_TRANS_DENY_TCP = 0x0005, |
| |
48 QQ_FILE_TRANS_NOTIFY = 0x003b, |
| |
49 QQ_FILE_TRANS_NOTIFY_ACK = 0x003c, |
| |
50 QQ_FILE_TRANS_CANCEL = 0x0049, |
| |
51 QQ_FILE_TRANS_PASV = 0x003f |
| |
52 }; |
| |
53 |
| |
54 static int _qq_in_same_lan(ft_info *info) |
| |
55 { |
| |
56 if (info->remote_internet_ip == info->local_internet_ip) return 1; |
| |
57 purple_debug(PURPLE_DEBUG_INFO, "QQ", |
| |
58 "Not in the same LAN, remote internet ip[%x], local internet ip[%x]\n", |
| |
59 info->remote_internet_ip |
| |
60 , info->local_internet_ip); |
| |
61 return 0; |
| |
62 } |
| |
63 |
| |
64 static int _qq_xfer_init_udp_channel(ft_info *info) |
| |
65 { |
| |
66 struct sockaddr_in sin; |
| |
67 memset(&sin, 0, sizeof(sin)); |
| |
68 sin.sin_family = AF_INET; |
| |
69 if (!_qq_in_same_lan(info)) { |
| |
70 sin.sin_port = g_htons(info->remote_major_port); |
| |
71 sin.sin_addr.s_addr = g_htonl(info->remote_internet_ip); |
| |
72 } else { |
| |
73 sin.sin_port = g_htons(info->remote_minor_port); |
| |
74 sin.sin_addr.s_addr = g_htonl(info->remote_real_ip); |
| |
75 } |
| |
76 return 0; |
| |
77 } |
| |
78 |
| |
79 /* these 2 functions send and recv buffer from/to UDP channel */ |
| |
80 static ssize_t _qq_xfer_udp_recv(guint8 *buf, size_t len, PurpleXfer *xfer) |
| |
81 { |
| |
82 struct sockaddr_in sin; |
| |
83 socklen_t sinlen; |
| |
84 ft_info *info; |
| |
85 gint r; |
| |
86 |
| |
87 info = (ft_info *) xfer->data; |
| |
88 sinlen = sizeof(sin); |
| |
89 r = recvfrom(info->recv_fd, buf, len, 0, (struct sockaddr *) &sin, &sinlen); |
| |
90 purple_debug(PURPLE_DEBUG_INFO, "QQ", |
| |
91 "==> recv %d bytes from File UDP Channel, remote ip[%s], remote port[%d]\n", |
| |
92 r, inet_ntoa(sin.sin_addr), g_ntohs(sin.sin_port)); |
| |
93 return r; |
| |
94 } |
| |
95 |
| |
96 /* |
| |
97 static ssize_t _qq_xfer_udp_send(const char *buf, size_t len, PurpleXfer *xfer) |
| |
98 { |
| |
99 ft_info *info; |
| |
100 |
| |
101 info = (ft_info *) xfer->data; |
| |
102 return send(info->sender_fd, buf, len, 0); |
| |
103 } |
| |
104 */ |
| |
105 static ssize_t _qq_xfer_udp_send(const guint8 *buf, size_t len, PurpleXfer *xfer) |
| |
106 { |
| |
107 struct sockaddr_in sin; |
| |
108 ft_info *info; |
| |
109 |
| |
110 info = (ft_info *) xfer->data; |
| |
111 memset(&sin, 0, sizeof(sin)); |
| |
112 sin.sin_family = AF_INET; |
| |
113 if (!_qq_in_same_lan(info)) { |
| |
114 sin.sin_port = g_htons(info->remote_major_port); |
| |
115 sin.sin_addr.s_addr = g_htonl(info->remote_internet_ip); |
| |
116 } else if (info->use_major) { |
| |
117 sin.sin_port = g_htons(info->remote_major_port); |
| |
118 sin.sin_addr.s_addr = g_htonl(info->remote_real_ip); |
| |
119 } else { |
| |
120 sin.sin_port = g_htons(info->remote_minor_port); |
| |
121 sin.sin_addr.s_addr = g_htonl(info->remote_real_ip); |
| |
122 } |
| |
123 purple_debug(PURPLE_DEBUG_INFO, "QQ", "sending to channel: %d.%d.%d.%d:%d\n", |
| |
124 sin.sin_addr.s_addr & 0xff, |
| |
125 (sin.sin_addr.s_addr >> 8) & 0xff, |
| |
126 (sin.sin_addr.s_addr >> 16) & 0xff, |
| |
127 sin.sin_addr.s_addr >> 24, |
| |
128 g_ntohs(sin.sin_port) |
| |
129 ); |
| |
130 return sendto(info->sender_fd, buf, len, 0, (struct sockaddr *) &sin, sizeof(sin)); |
| |
131 } |
| |
132 |
| |
133 /* user-defined functions for purple_xfer_read and purple_xfer_write */ |
| |
134 |
| |
135 /* |
| |
136 static ssize_t _qq_xfer_read(char **buf, PurpleXfer *xfer) |
| |
137 { |
| |
138 *buf = g_newa(char, QQ_FILE_FRAGMENT_MAXLEN + 100); |
| |
139 return _qq_xfer_udp_recv(*buf, QQ_FILE_FRAGMENT_MAXLEN + 100, xfer); |
| |
140 } |
| |
141 */ |
| |
142 |
| |
143 gssize _qq_xfer_write(const guint8 *buf, size_t len, PurpleXfer *xfer) |
| |
144 { |
| |
145 return _qq_xfer_udp_send(buf, len, xfer); |
| |
146 } |
| |
147 |
| |
148 static void _qq_xfer_recv_packet(gpointer data, gint source, PurpleInputCondition condition) |
| |
149 { |
| |
150 PurpleXfer *xfer = (PurpleXfer *) data; |
| |
151 PurpleAccount *account = purple_xfer_get_account(xfer); |
| |
152 PurpleConnection *gc = purple_account_get_connection(account); |
| |
153 guint8 *buf; |
| |
154 gint size; |
| |
155 /* FIXME: It seems that the transfer never use a packet |
| |
156 * larger than 1500 bytes, so if it happened to be a |
| |
157 * larger packet, either error occured or protocol should |
| |
158 * be modified |
| |
159 */ |
| |
160 ft_info *info; |
| |
161 info = xfer->data; |
| |
162 g_return_if_fail (source == info->recv_fd); |
| |
163 buf = g_newa(guint8, 1500); |
| |
164 size = _qq_xfer_udp_recv(buf, 1500, xfer); |
| |
165 qq_process_recv_file(gc, buf, size); |
| |
166 } |
| |
167 |
| |
168 /* start file transfer process */ |
| |
169 /* |
| |
170 static void _qq_xfer_send_start (PurpleXfer *xfer) |
| |
171 { |
| |
172 PurpleAccount *account; |
| |
173 PurpleConnection *gc; |
| |
174 ft_info *info; |
| |
175 |
| |
176 account = purple_xfer_get_account(xfer); |
| |
177 gc = purple_account_get_connection(account); |
| |
178 info = (ft_info *) xfer->data; |
| |
179 } |
| |
180 */ |
| |
181 |
| |
182 /* |
| |
183 static void _qq_xfer_send_ack (PurpleXfer *xfer, const char *buffer, size_t len) |
| |
184 { |
| |
185 PurpleAccount *account; |
| |
186 PurpleConnection *gc; |
| |
187 |
| |
188 account = purple_xfer_get_account(xfer); |
| |
189 gc = purple_account_get_connection(account); |
| |
190 qq_process_recv_file(gc, (guint8 *) buffer, len); |
| |
191 } |
| |
192 */ |
| |
193 |
| |
194 /* |
| |
195 static void _qq_xfer_recv_start(PurpleXfer *xfer) |
| |
196 { |
| |
197 } |
| |
198 */ |
| |
199 |
| |
200 static void _qq_xfer_end(PurpleXfer *xfer) |
| |
201 { |
| |
202 ft_info *info; |
| |
203 g_return_if_fail(xfer != NULL && xfer->data != NULL); |
| |
204 info = (ft_info *) xfer->data; |
| |
205 |
| |
206 qq_xfer_close_file(xfer); |
| |
207 if (info->dest_fp != NULL) { |
| |
208 fclose(info->dest_fp); |
| |
209 purple_debug(PURPLE_DEBUG_INFO, "QQ", "file closed\n"); |
| |
210 } |
| |
211 if (info->major_fd != 0) { |
| |
212 close(info->major_fd); |
| |
213 purple_debug(PURPLE_DEBUG_INFO, "QQ", "major port closed\n"); |
| |
214 } |
| |
215 if (info->minor_fd != 0) { |
| |
216 close(info->minor_fd); |
| |
217 purple_debug(PURPLE_DEBUG_INFO, "QQ", "minor port closed\n"); |
| |
218 } |
| |
219 /* |
| |
220 if (info->buffer != NULL) { |
| |
221 munmap(info->buffer, purple_xfer_get_size(xfer)); |
| |
222 purple_debug(PURPLE_DEBUG_INFO, "QQ", "file mapping buffer is freed.\n"); |
| |
223 } |
| |
224 */ |
| |
225 g_free(info); |
| |
226 } |
| |
227 |
| |
228 static void qq_show_conn_info(ft_info *info) |
| |
229 { |
| |
230 gchar *internet_ip_str, *real_ip_str; |
| |
231 guint32 ip; |
| |
232 |
| |
233 ip = g_htonl(info->remote_real_ip); |
| |
234 real_ip_str = gen_ip_str((guint8 *) &ip); |
| |
235 ip = g_htonl(info->remote_internet_ip); |
| |
236 internet_ip_str = gen_ip_str((guint8 *) &ip); |
| |
237 purple_debug(PURPLE_DEBUG_INFO, "QQ", "remote internet ip[%s:%d], major port[%d], real ip[%s], minor port[%d]\n", |
| |
238 internet_ip_str, info->remote_internet_port, |
| |
239 info->remote_major_port, real_ip_str, info->remote_minor_port |
| |
240 ); |
| |
241 g_free(real_ip_str); |
| |
242 g_free(internet_ip_str); |
| |
243 } |
| |
244 |
| |
245 void qq_get_conn_info(guint8 *data, guint8 **cursor, gint data_len, ft_info *info) |
| |
246 { |
| |
247 read_packet_data(data, cursor, data_len, info->file_session_key, 16); |
| |
248 *cursor += 30; |
| |
249 read_packet_b(data, cursor, data_len, &info->conn_method); |
| |
250 read_packet_dw(data, cursor, data_len, &info->remote_internet_ip); |
| |
251 read_packet_w(data, cursor, data_len, &info->remote_internet_port); |
| |
252 read_packet_w(data, cursor, data_len, &info->remote_major_port); |
| |
253 read_packet_dw(data, cursor, data_len, &info->remote_real_ip); |
| |
254 read_packet_w(data, cursor, data_len, &info->remote_minor_port); |
| |
255 qq_show_conn_info(info); |
| |
256 } |
| |
257 |
| |
258 gint qq_fill_conn_info(guint8 *raw_data, guint8 **cursor, ft_info *info) |
| |
259 { |
| |
260 gint bytes; |
| |
261 bytes = 0; |
| |
262 /* 064: connection method, UDP 0x00, TCP 0x03 */ |
| |
263 bytes += create_packet_b (raw_data, cursor, info->conn_method); |
| |
264 /* 065-068: outer ip address of sender (proxy address) */ |
| |
265 bytes += create_packet_dw (raw_data, cursor, info->local_internet_ip); |
| |
266 /* 069-070: sender port */ |
| |
267 bytes += create_packet_w (raw_data, cursor, info->local_internet_port); |
| |
268 /* 071-072: the first listening port(TCP doesn't have this part) */ |
| |
269 bytes += create_packet_w (raw_data, cursor, info->local_major_port); |
| |
270 /* 073-076: real ip */ |
| |
271 bytes += create_packet_dw (raw_data, cursor, info->local_real_ip); |
| |
272 /* 077-078: the second listening port */ |
| |
273 bytes += create_packet_w (raw_data, cursor, info->local_minor_port); |
| |
274 return bytes; |
| |
275 } |
| |
276 |
| |
277 |
| |
278 /* fill in the common information of file transfer */ |
| |
279 static gint _qq_create_packet_file_header |
| |
280 (guint8 *raw_data, guint8 **cursor, guint32 to_uid, guint16 message_type, qq_data *qd, gboolean seq_ack) |
| |
281 { |
| |
282 gint bytes; |
| |
283 time_t now; |
| |
284 guint16 seq; |
| |
285 ft_info *info; |
| |
286 |
| |
287 bytes = 0; |
| |
288 now = time(NULL); |
| |
289 if (!seq_ack) seq = qd->send_seq; |
| |
290 else { |
| |
291 info = (ft_info *) qd->xfer->data; |
| |
292 seq = info->send_seq; |
| |
293 } |
| |
294 |
| |
295 /* 000-003: receiver uid */ |
| |
296 bytes += create_packet_dw (raw_data, cursor, qd->uid); |
| |
297 /* 004-007: sender uid */ |
| |
298 bytes += create_packet_dw (raw_data, cursor, to_uid); |
| |
299 /* 008-009: sender client version */ |
| |
300 bytes += create_packet_w (raw_data, cursor, QQ_CLIENT); |
| |
301 /* 010-013: receiver uid */ |
| |
302 bytes += create_packet_dw (raw_data, cursor, qd->uid); |
| |
303 /* 014-017: sender uid */ |
| |
304 bytes += create_packet_dw (raw_data, cursor, to_uid); |
| |
305 /* 018-033: md5 of (uid+session_key) */ |
| |
306 bytes += create_packet_data (raw_data, cursor, qd->session_md5, 16); |
| |
307 /* 034-035: message type */ |
| |
308 bytes += create_packet_w (raw_data, cursor, message_type); |
| |
309 /* 036-037: sequence number */ |
| |
310 bytes += create_packet_w (raw_data, cursor, seq); |
| |
311 /* 038-041: send time */ |
| |
312 bytes += create_packet_dw (raw_data, cursor, (guint32) now); |
| |
313 /* 042-042: always 0x00 */ |
| |
314 bytes += create_packet_b (raw_data, cursor, 0x00); |
| |
315 /* 043-043: sender icon */ |
| |
316 bytes += create_packet_b (raw_data, cursor, qd->my_icon); |
| |
317 /* 044-046: always 0x00 */ |
| |
318 bytes += create_packet_w (raw_data, cursor, 0x0000); |
| |
319 bytes += create_packet_b (raw_data, cursor, 0x00); |
| |
320 /* 047-047: we use font attr */ |
| |
321 bytes += create_packet_b (raw_data, cursor, 0x01); |
| |
322 /* 048-051: always 0x00 */ |
| |
323 bytes += create_packet_dw (raw_data, cursor, 0x00000000); |
| |
324 |
| |
325 /* 052-062: always 0x00 */ |
| |
326 bytes += create_packet_dw (raw_data, cursor, 0x00000000); |
| |
327 bytes += create_packet_dw (raw_data, cursor, 0x00000000); |
| |
328 bytes += create_packet_w (raw_data, cursor, 0x0000); |
| |
329 bytes += create_packet_b (raw_data, cursor, 0x00); |
| |
330 /* 063: transfer_type, 0x65: FILE 0x6b: FACE */ |
| |
331 bytes += create_packet_b (raw_data, cursor, QQ_FILE_TRANSFER_FILE); /* FIXME */ |
| |
332 |
| |
333 return bytes; |
| |
334 } |
| |
335 |
| |
336 #if 0 |
| |
337 in_addr_t get_real_ip() |
| |
338 { |
| |
339 char hostname[40]; |
| |
340 struct hostent *host; |
| |
341 |
| |
342 gethostname(hostname, sizeof(hostname)); |
| |
343 host = gethostbyname(hostname); |
| |
344 return *(host->h_addr); |
| |
345 } |
| |
346 |
| |
347 |
| |
348 #include <sys/ioctl.h> |
| |
349 #include <net/if.h> |
| |
350 |
| |
351 #define MAXINTERFACES 16 |
| |
352 in_addr_t get_real_ip() |
| |
353 { |
| |
354 int fd, intrface, i; |
| |
355 struct ifconf ifc; |
| |
356 struct ifreq buf[MAXINTERFACES]; |
| |
357 in_addr_t ret; |
| |
358 |
| |
359 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) return 0; |
| |
360 ifc.ifc_len = sizeof(buf); |
| |
361 ifc.ifc_buf = (caddr_t) buf; |
| |
362 if (ioctl(fd, SIOCGIFCONF, (char *) &ifc) < 0) return 0; |
| |
363 intrface = ifc.ifc_len / sizeof(struct ifreq); |
| |
364 for (i = 0; i < intrface; i++) { |
| |
365 //buf[intrface].ifr_name |
| |
366 if (ioctl(fd, SIOCGIFADDR, (char *) &buf[i]) >= 0) |
| |
367 { |
| |
368 ret = (((struct sockaddr_in *)(&buf[i].ifr_addr))->sin_addr).s_addr; |
| |
369 if (ret == g_ntohl(0x7f000001)) continue; |
| |
370 return ret; |
| |
371 } |
| |
372 } |
| |
373 return 0; |
| |
374 } |
| |
375 #endif |
| |
376 |
| |
377 static void _qq_xfer_init_socket(PurpleXfer *xfer) |
| |
378 { |
| |
379 gint sockfd, listen_port = 0, i; |
| |
380 socklen_t sin_len; |
| |
381 struct sockaddr_in sin; |
| |
382 ft_info *info; |
| |
383 |
| |
384 g_return_if_fail(xfer != NULL); |
| |
385 g_return_if_fail(xfer->data != NULL); |
| |
386 info = (ft_info *) xfer->data; |
| |
387 |
| |
388 /* debug |
| |
389 info->local_real_ip = 0x7f000001; |
| |
390 */ |
| |
391 info->local_real_ip = g_ntohl(inet_addr(purple_network_get_my_ip(-1))); |
| |
392 purple_debug(PURPLE_DEBUG_INFO, "QQ", "local real ip is %x", info->local_real_ip); |
| |
393 |
| |
394 for (i = 0; i < 2; i++) { |
| |
395 sockfd = socket(PF_INET, SOCK_DGRAM, 0); |
| |
396 g_return_if_fail(sockfd >= 0); |
| |
397 |
| |
398 memset(&sin, 0, sizeof(sin)); |
| |
399 sin.sin_family = AF_INET; |
| |
400 sin.sin_port = 0; |
| |
401 sin.sin_addr.s_addr = INADDR_ANY; |
| |
402 sin_len = sizeof(sin); |
| |
403 bind(sockfd, (struct sockaddr *) &sin, sin_len); |
| |
404 getsockname(sockfd, (struct sockaddr *) &sin, &sin_len); |
| |
405 listen_port = g_ntohs(sin.sin_port); |
| |
406 |
| |
407 switch (i) { |
| |
408 case 0: |
| |
409 info->local_major_port = listen_port; |
| |
410 info->major_fd = sockfd; |
| |
411 purple_debug(PURPLE_DEBUG_INFO, "QQ", "UDP Major Channel created on port[%d]\n", |
| |
412 info->local_major_port); |
| |
413 break; |
| |
414 case 1: |
| |
415 info->local_minor_port = listen_port; |
| |
416 info->minor_fd = sockfd; |
| |
417 purple_debug(PURPLE_DEBUG_INFO, "QQ", "UDP Minor Channel created on port[%d]\n", |
| |
418 info->local_minor_port); |
| |
419 break; |
| |
420 } |
| |
421 } |
| |
422 |
| |
423 if (_qq_in_same_lan(info)) { |
| |
424 info->sender_fd = info->recv_fd = info->minor_fd; |
| |
425 } else { |
| |
426 info->sender_fd = info->recv_fd = info->major_fd; |
| |
427 } |
| |
428 /* xfer->watcher = purple_input_add(info->recv_fd, PURPLE_INPUT_READ, _qq_xfer_recv_packet, xfer); */ |
| |
429 } |
| |
430 |
| |
431 /* create the QQ_FILE_TRANS_REQ packet with file infomations */ |
| |
432 static void _qq_send_packet_file_request (PurpleConnection *gc, guint32 to_uid, gchar *filename, gint filesize) |
| |
433 { |
| |
434 qq_data *qd; |
| |
435 guint8 *cursor, *raw_data; |
| |
436 gchar *filelen_str; |
| |
437 gint filename_len, filelen_strlen, packet_len, bytes; |
| |
438 ft_info *info; |
| |
439 |
| |
440 qd = (qq_data *) gc->proto_data; |
| |
441 |
| |
442 info = g_new0(ft_info, 1); |
| |
443 info->to_uid = to_uid; |
| |
444 info->send_seq = qd->send_seq; |
| |
445 info->local_internet_ip = g_ntohl(inet_addr(qd->my_ip)); |
| |
446 info->local_internet_port = qd->my_port; |
| |
447 info->local_real_ip = 0x00000000; |
| |
448 info->conn_method = 0x00; |
| |
449 qd->xfer->data = info; |
| |
450 |
| |
451 filename_len = strlen(filename); |
| |
452 filelen_str = g_strdup_printf("%d ?Ö½?", filesize); |
| |
453 filelen_strlen = strlen(filelen_str); |
| |
454 |
| |
455 packet_len = 82 + filename_len + filelen_strlen; |
| |
456 raw_data = g_newa(guint8, packet_len); |
| |
457 cursor = raw_data; |
| |
458 |
| |
459 bytes = _qq_create_packet_file_header(raw_data, &cursor, to_uid, |
| |
460 QQ_FILE_TRANS_REQ, qd, FALSE); |
| |
461 bytes += qq_fill_conn_info(raw_data, &cursor, info); |
| |
462 /* 079: 0x20 */ |
| |
463 bytes += create_packet_b (raw_data, &cursor, 0x20); |
| |
464 /* 080: 0x1f */ |
| |
465 bytes += create_packet_b (raw_data, &cursor, 0x1f); |
| |
466 /* undetermined len: filename */ |
| |
467 bytes += create_packet_data (raw_data, &cursor, (guint8 *) filename, |
| |
468 filename_len); |
| |
469 /* 0x1f */ |
| |
470 bytes += create_packet_b (raw_data, &cursor, 0x1f); |
| |
471 /* file length */ |
| |
472 bytes += create_packet_data (raw_data, &cursor, (guint8 *) filelen_str, |
| |
473 filelen_strlen); |
| |
474 |
| |
475 if (packet_len == bytes) |
| |
476 qq_send_cmd (gc, QQ_CMD_SEND_IM, TRUE, 0, TRUE, raw_data, |
| |
477 cursor - raw_data); |
| |
478 else |
| |
479 purple_debug (PURPLE_DEBUG_INFO, "qq_send_packet_file_request", |
| |
480 "%d bytes expected but got %d bytes\n", |
| |
481 packet_len, bytes); |
| |
482 |
| |
483 g_free (filelen_str); |
| |
484 } |
| |
485 |
| |
486 /* tell the buddy we want to accept the file */ |
| |
487 static void _qq_send_packet_file_accept(PurpleConnection *gc, guint32 to_uid) |
| |
488 { |
| |
489 qq_data *qd; |
| |
490 guint8 *cursor, *raw_data; |
| |
491 guint16 minor_port; |
| |
492 guint32 real_ip; |
| |
493 gint packet_len, bytes; |
| |
494 ft_info *info; |
| |
495 |
| |
496 qd = (qq_data *) gc->proto_data; |
| |
497 info = (ft_info *) qd->xfer->data; |
| |
498 |
| |
499 purple_debug(PURPLE_DEBUG_INFO, "QQ", "I've accepted the file transfer request from %d\n", to_uid); |
| |
500 _qq_xfer_init_socket(qd->xfer); |
| |
501 |
| |
502 packet_len = 79; |
| |
503 raw_data = g_newa (guint8, packet_len); |
| |
504 cursor = raw_data; |
| |
505 |
| |
506 minor_port = info->local_minor_port; |
| |
507 real_ip = info->local_real_ip; |
| |
508 info->local_minor_port = 0; |
| |
509 info->local_real_ip = 0; |
| |
510 |
| |
511 bytes = _qq_create_packet_file_header(raw_data, &cursor, to_uid, QQ_FILE_TRANS_ACC_UDP, qd, TRUE); |
| |
512 bytes += qq_fill_conn_info(raw_data, &cursor, info); |
| |
513 |
| |
514 info->local_minor_port = minor_port; |
| |
515 info->local_real_ip = real_ip; |
| |
516 |
| |
517 if (packet_len == bytes) |
| |
518 qq_send_cmd (gc, QQ_CMD_SEND_IM, TRUE, 0, TRUE, raw_data, |
| |
519 cursor - raw_data); |
| |
520 else |
| |
521 purple_debug (PURPLE_DEBUG_INFO, "qq_send_packet_file_accept", |
| |
522 "%d bytes expected but got %d bytes\n", |
| |
523 packet_len, bytes); |
| |
524 } |
| |
525 |
| |
526 static void _qq_send_packet_file_notifyip(PurpleConnection *gc, guint32 to_uid) |
| |
527 { |
| |
528 PurpleXfer *xfer; |
| |
529 ft_info *info; |
| |
530 qq_data *qd; |
| |
531 guint8 *cursor, *raw_data; |
| |
532 gint packet_len, bytes; |
| |
533 |
| |
534 qd = (qq_data *) gc->proto_data; |
| |
535 xfer = qd->xfer; |
| |
536 info = xfer->data; |
| |
537 |
| |
538 packet_len = 79; |
| |
539 raw_data = g_newa (guint8, packet_len); |
| |
540 cursor = raw_data; |
| |
541 |
| |
542 purple_debug(PURPLE_DEBUG_INFO, "QQ", "<== sending qq file notify ip packet\n"); |
| |
543 bytes = _qq_create_packet_file_header(raw_data, &cursor, to_uid, QQ_FILE_TRANS_NOTIFY, qd, TRUE); |
| |
544 bytes += qq_fill_conn_info(raw_data, &cursor, info); |
| |
545 if (packet_len == bytes) |
| |
546 qq_send_cmd (gc, QQ_CMD_SEND_IM, TRUE, 0, TRUE, raw_data, |
| |
547 cursor - raw_data); |
| |
548 else |
| |
549 purple_debug (PURPLE_DEBUG_INFO, "qq_send_packet_file_notify", |
| |
550 "%d bytes expected but got %d bytes\n", |
| |
551 packet_len, bytes); |
| |
552 |
| |
553 if (xfer->watcher) purple_input_remove(xfer->watcher); |
| |
554 xfer->watcher = purple_input_add(info->recv_fd, PURPLE_INPUT_READ, _qq_xfer_recv_packet, xfer); |
| |
555 purple_input_add(info->major_fd, PURPLE_INPUT_READ, _qq_xfer_recv_packet, xfer); |
| |
556 } |
| |
557 |
| |
558 /* tell the buddy we don't want the file */ |
| |
559 static void _qq_send_packet_file_reject (PurpleConnection *gc, guint32 to_uid) |
| |
560 { |
| |
561 qq_data *qd; |
| |
562 guint8 *cursor, *raw_data; |
| |
563 gint packet_len, bytes; |
| |
564 |
| |
565 purple_debug(PURPLE_DEBUG_INFO, "_qq_send_packet_file_reject", "start"); |
| |
566 qd = (qq_data *) gc->proto_data; |
| |
567 |
| |
568 packet_len = 64; |
| |
569 raw_data = g_newa (guint8, packet_len); |
| |
570 cursor = raw_data; |
| |
571 bytes = 0; |
| |
572 |
| |
573 bytes = _qq_create_packet_file_header(raw_data, &cursor, to_uid, QQ_FILE_TRANS_DENY_UDP, qd, TRUE); |
| |
574 |
| |
575 if (packet_len == bytes) |
| |
576 qq_send_cmd (gc, QQ_CMD_SEND_IM, TRUE, 0, TRUE, raw_data, |
| |
577 cursor - raw_data); |
| |
578 else |
| |
579 purple_debug (PURPLE_DEBUG_INFO, "qq_send_packet_file", |
| |
580 "%d bytes expected but got %d bytes\n", |
| |
581 packet_len, bytes); |
| |
582 } |
| |
583 |
| |
584 /* tell the buddy to cancel transfer */ |
| |
585 static void _qq_send_packet_file_cancel (PurpleConnection *gc, guint32 to_uid) |
| |
586 { |
| |
587 qq_data *qd; |
| |
588 guint8 *cursor, *raw_data; |
| |
589 gint packet_len, bytes; |
| |
590 |
| |
591 purple_debug(PURPLE_DEBUG_INFO, "_qq_send_packet_file_cancel", "start\n"); |
| |
592 qd = (qq_data *) gc->proto_data; |
| |
593 |
| |
594 packet_len = 64; |
| |
595 raw_data = g_newa (guint8, packet_len); |
| |
596 cursor = raw_data; |
| |
597 bytes = 0; |
| |
598 |
| |
599 purple_debug(PURPLE_DEBUG_INFO, "_qq_send_packet_file_cancel", "before create header\n"); |
| |
600 bytes = _qq_create_packet_file_header(raw_data, &cursor, to_uid, QQ_FILE_TRANS_CANCEL, qd, TRUE); |
| |
601 purple_debug(PURPLE_DEBUG_INFO, "_qq_send_packet_file_cancel", "end create header\n"); |
| |
602 |
| |
603 if (packet_len == bytes) { |
| |
604 purple_debug(PURPLE_DEBUG_INFO, "_qq_send_packet_file_cancel", "before send cmd\n"); |
| |
605 qq_send_cmd (gc, QQ_CMD_SEND_IM, TRUE, 0, TRUE, raw_data, |
| |
606 cursor - raw_data); |
| |
607 } |
| |
608 else |
| |
609 purple_debug (PURPLE_DEBUG_INFO, "qq_send_packet_file", |
| |
610 "%d bytes expected but got %d bytes\n", |
| |
611 packet_len, bytes); |
| |
612 |
| |
613 purple_debug (PURPLE_DEBUG_INFO, "qq_send_packet_file_cancel", "end\n"); |
| |
614 } |
| |
615 |
| |
616 /* request to send a file */ |
| |
617 static void |
| |
618 _qq_xfer_init (PurpleXfer * xfer) |
| |
619 { |
| |
620 PurpleConnection *gc; |
| |
621 PurpleAccount *account; |
| |
622 guint32 to_uid; |
| |
623 gchar *filename, *filename_without_path; |
| |
624 |
| |
625 g_return_if_fail (xfer != NULL); |
| |
626 account = purple_xfer_get_account(xfer); |
| |
627 gc = purple_account_get_connection(account); |
| |
628 |
| |
629 to_uid = purple_name_to_uid (xfer->who); |
| |
630 g_return_if_fail (to_uid != 0); |
| |
631 |
| |
632 filename = (gchar *) purple_xfer_get_local_filename (xfer); |
| |
633 g_return_if_fail (filename != NULL); |
| |
634 |
| |
635 filename_without_path = strrchr (filename, '/') + 1; |
| |
636 |
| |
637 _qq_send_packet_file_request (gc, to_uid, filename_without_path, |
| |
638 purple_xfer_get_size(xfer)); |
| |
639 } |
| |
640 |
| |
641 /* cancel the transfer of receiving files */ |
| |
642 static void _qq_xfer_cancel(PurpleXfer *xfer) |
| |
643 { |
| |
644 PurpleConnection *gc; |
| |
645 PurpleAccount *account; |
| |
646 guint16 *seq; |
| |
647 |
| |
648 g_return_if_fail (xfer != NULL); |
| |
649 seq = (guint16 *) xfer->data; |
| |
650 account = purple_xfer_get_account(xfer); |
| |
651 gc = purple_account_get_connection(account); |
| |
652 |
| |
653 switch (purple_xfer_get_status(xfer)) { |
| |
654 case PURPLE_XFER_STATUS_CANCEL_LOCAL: |
| |
655 _qq_send_packet_file_cancel(gc, purple_name_to_uid(xfer->who)); |
| |
656 break; |
| |
657 case PURPLE_XFER_STATUS_CANCEL_REMOTE: |
| |
658 _qq_send_packet_file_cancel(gc, purple_name_to_uid(xfer->who)); |
| |
659 break; |
| |
660 case PURPLE_XFER_STATUS_NOT_STARTED: |
| |
661 break; |
| |
662 case PURPLE_XFER_STATUS_UNKNOWN: |
| |
663 _qq_send_packet_file_reject(gc, purple_name_to_uid(xfer->who)); |
| |
664 break; |
| |
665 case PURPLE_XFER_STATUS_DONE: |
| |
666 break; |
| |
667 case PURPLE_XFER_STATUS_ACCEPTED: |
| |
668 break; |
| |
669 case PURPLE_XFER_STATUS_STARTED: |
| |
670 break; |
| |
671 } |
| |
672 } |
| |
673 |
| |
674 /* init the transfer of receiving files */ |
| |
675 static void _qq_xfer_recv_init(PurpleXfer *xfer) |
| |
676 { |
| |
677 PurpleConnection *gc; |
| |
678 PurpleAccount *account; |
| |
679 ft_info *info; |
| |
680 |
| |
681 g_return_if_fail (xfer != NULL && xfer->data != NULL); |
| |
682 info = (ft_info *) xfer->data; |
| |
683 account = purple_xfer_get_account(xfer); |
| |
684 gc = purple_account_get_connection(account); |
| |
685 |
| |
686 _qq_send_packet_file_accept(gc, purple_name_to_uid(xfer->who)); |
| |
687 } |
| |
688 |
| |
689 /* process reject im for file transfer request */ |
| |
690 void qq_process_recv_file_reject (guint8 *data, guint8 **cursor, gint data_len, |
| |
691 guint32 sender_uid, PurpleConnection *gc) |
| |
692 { |
| |
693 gchar *msg, *filename; |
| |
694 qq_data *qd; |
| |
695 |
| |
696 g_return_if_fail (data != NULL && data_len != 0); |
| |
697 qd = (qq_data *) gc->proto_data; |
| |
698 g_return_if_fail (qd->xfer != NULL); |
| |
699 |
| |
700 if (*cursor >= (data + data_len - 1)) { |
| |
701 purple_debug (PURPLE_DEBUG_WARNING, "QQ", |
| |
702 "Received file reject message is empty\n"); |
| |
703 return; |
| |
704 } |
| |
705 filename = strrchr(purple_xfer_get_local_filename(qd->xfer), '/') + 1; |
| |
706 msg = g_strdup_printf(_("%d has declined the file %s"), |
| |
707 sender_uid, filename); |
| |
708 |
| |
709 purple_notify_warning (gc, _("File Send"), msg, NULL); |
| |
710 purple_xfer_request_denied(qd->xfer); |
| |
711 qd->xfer = NULL; |
| |
712 |
| |
713 g_free (msg); |
| |
714 } |
| |
715 |
| |
716 /* process cancel im for file transfer request */ |
| |
717 void qq_process_recv_file_cancel (guint8 *data, guint8 **cursor, gint data_len, |
| |
718 guint32 sender_uid, PurpleConnection *gc) |
| |
719 { |
| |
720 gchar *msg, *filename; |
| |
721 qq_data *qd; |
| |
722 |
| |
723 g_return_if_fail (data != NULL && data_len != 0); |
| |
724 qd = (qq_data *) gc->proto_data; |
| |
725 g_return_if_fail (qd->xfer != NULL |
| |
726 && purple_xfer_get_filename(qd->xfer) != NULL); |
| |
727 |
| |
728 if (*cursor >= (data + data_len - 1)) { |
| |
729 purple_debug (PURPLE_DEBUG_WARNING, "QQ", |
| |
730 "Received file reject message is empty\n"); |
| |
731 return; |
| |
732 } |
| |
733 filename = strrchr(purple_xfer_get_local_filename(qd->xfer), '/') + 1; |
| |
734 msg = g_strdup_printf |
| |
735 (_("%d canceled the transfer of %s"), |
| |
736 sender_uid, filename); |
| |
737 |
| |
738 purple_notify_warning (gc, _("File Send"), msg, NULL); |
| |
739 purple_xfer_cancel_remote(qd->xfer); |
| |
740 qd->xfer = NULL; |
| |
741 |
| |
742 g_free (msg); |
| |
743 } |
| |
744 |
| |
745 /* process accept im for file transfer request */ |
| |
746 void qq_process_recv_file_accept(guint8 *data, guint8 **cursor, gint data_len, |
| |
747 guint32 sender_uid, PurpleConnection *gc) |
| |
748 { |
| |
749 qq_data *qd; |
| |
750 ft_info *info; |
| |
751 PurpleXfer *xfer; |
| |
752 |
| |
753 g_return_if_fail (data != NULL && data_len != 0); |
| |
754 qd = (qq_data *) gc->proto_data; |
| |
755 xfer = qd->xfer; |
| |
756 |
| |
757 if (*cursor >= (data + data_len - 1)) { |
| |
758 purple_debug (PURPLE_DEBUG_WARNING, "QQ", |
| |
759 "Received file reject message is empty\n"); |
| |
760 return; |
| |
761 } |
| |
762 |
| |
763 info = (ft_info *) qd->xfer->data; |
| |
764 |
| |
765 *cursor = data + 18 + 12; |
| |
766 qq_get_conn_info(data, cursor, data_len, info); |
| |
767 _qq_xfer_init_socket(qd->xfer); |
| |
768 |
| |
769 _qq_xfer_init_udp_channel(info); |
| |
770 _qq_send_packet_file_notifyip(gc, sender_uid); |
| |
771 } |
| |
772 |
| |
773 /* process request from buddy's im for file transfer request */ |
| |
774 void qq_process_recv_file_request(guint8 *data, guint8 **cursor, gint data_len, |
| |
775 guint32 sender_uid, PurpleConnection * gc) |
| |
776 { |
| |
777 qq_data *qd; |
| |
778 PurpleXfer *xfer; |
| |
779 gchar *sender_name, **fileinfo; |
| |
780 ft_info *info; |
| |
781 PurpleBuddy *b; |
| |
782 qq_buddy *q_bud; |
| |
783 |
| |
784 g_return_if_fail (data != NULL && data_len != 0); |
| |
785 qd = (qq_data *) gc->proto_data; |
| |
786 |
| |
787 if (*cursor >= (data + data_len - 1)) { |
| |
788 purple_debug (PURPLE_DEBUG_WARNING, "QQ", |
| |
789 "Received file reject message is empty\n"); |
| |
790 return; |
| |
791 } |
| |
792 |
| |
793 info = g_new0(ft_info, 1); |
| |
794 info->local_internet_ip = g_ntohl(inet_addr(qd->my_ip)); |
| |
795 info->local_internet_port = qd->my_port; |
| |
796 info->local_real_ip = 0x00000000; |
| |
797 info->to_uid = sender_uid; |
| |
798 read_packet_w(data, cursor, data_len, &(info->send_seq)); |
| |
799 |
| |
800 *cursor = data + 18 + 12; |
| |
801 qq_get_conn_info(data, cursor, data_len, info); |
| |
802 |
| |
803 fileinfo = g_strsplit((gchar *) (data + 81 + 12), "\x1f", 2); |
| |
804 g_return_if_fail (fileinfo != NULL && fileinfo[0] != NULL && fileinfo[1] != NULL); |
| |
805 |
| |
806 sender_name = uid_to_purple_name(sender_uid); |
| |
807 |
| |
808 /* FACE from IP detector, ignored by gfhuang */ |
| |
809 if(g_ascii_strcasecmp(fileinfo[0], "FACE") == 0) { |
| |
810 purple_debug(PURPLE_DEBUG_WARNING, "QQ", |
| |
811 "Received a FACE ip detect from qq-%d, so he/she must be online :)\n", sender_uid); |
| |
812 |
| |
813 b = purple_find_buddy(gc->account, sender_name); |
| |
814 q_bud = (b == NULL) ? NULL : (qq_buddy *) b->proto_data; |
| |
815 if (q_bud) { |
| |
816 if(0 != info->remote_real_ip) { |
| |
817 g_memmove(q_bud->ip, &info->remote_real_ip, 4); |
| |
818 q_bud->port = info->remote_minor_port; |
| |
819 } |
| |
820 else if (0 != info->remote_internet_ip) { |
| |
821 g_memmove(q_bud->ip, &info->remote_internet_ip, 4); |
| |
822 q_bud->port = info->remote_major_port; |
| |
823 } |
| |
824 |
| |
825 if(!is_online(q_bud->status)) { |
| |
826 q_bud->status = QQ_BUDDY_ONLINE_INVISIBLE; |
| |
827 qq_update_buddy_contact(gc, q_bud); |
| |
828 } |
| |
829 else |
| |
830 purple_debug(PURPLE_DEBUG_INFO, "QQ", "buddy %d is already online\n", sender_uid); |
| |
831 |
| |
832 } |
| |
833 else |
| |
834 purple_debug(PURPLE_DEBUG_WARNING, "QQ", "buddy %d is not in my friendlist\n", sender_uid); |
| |
835 |
| |
836 g_free(sender_name); |
| |
837 g_strfreev(fileinfo); |
| |
838 return; |
| |
839 } |
| |
840 |
| |
841 xfer = purple_xfer_new(purple_connection_get_account(gc), |
| |
842 PURPLE_XFER_RECEIVE, |
| |
843 sender_name); |
| |
844 if (xfer) |
| |
845 { |
| |
846 purple_xfer_set_filename(xfer, fileinfo[0]); |
| |
847 purple_xfer_set_size(xfer, atoi(fileinfo[1])); |
| |
848 |
| |
849 purple_xfer_set_init_fnc(xfer, _qq_xfer_recv_init); |
| |
850 purple_xfer_set_request_denied_fnc(xfer, _qq_xfer_cancel); |
| |
851 purple_xfer_set_cancel_recv_fnc(xfer, _qq_xfer_cancel); |
| |
852 purple_xfer_set_end_fnc(xfer, _qq_xfer_end); |
| |
853 purple_xfer_set_write_fnc(xfer, _qq_xfer_write); |
| |
854 |
| |
855 xfer->data = info; |
| |
856 qd->xfer = xfer; |
| |
857 |
| |
858 purple_xfer_request(xfer); |
| |
859 } |
| |
860 |
| |
861 g_free(sender_name); |
| |
862 g_strfreev(fileinfo); |
| |
863 } |
| |
864 |
| |
865 static void _qq_xfer_send_notify_ip_ack(gpointer data, gint source, PurpleInputCondition cond) |
| |
866 { |
| |
867 PurpleXfer *xfer = (PurpleXfer *) data; |
| |
868 PurpleAccount *account = purple_xfer_get_account(xfer); |
| |
869 PurpleConnection *gc = purple_account_get_connection(account); |
| |
870 ft_info *info = (ft_info *) xfer->data; |
| |
871 |
| |
872 purple_input_remove(xfer->watcher); |
| |
873 xfer->watcher = purple_input_add(info->recv_fd, PURPLE_INPUT_READ, _qq_xfer_recv_packet, xfer); |
| |
874 qq_send_file_ctl_packet(gc, QQ_FILE_CMD_NOTIFY_IP_ACK, info->to_uid, 0); |
| |
875 /* |
| |
876 info->use_major = TRUE; |
| |
877 qq_send_file_ctl_packet(gc, QQ_FILE_CMD_NOTIFY_IP_ACK, info->to_uid, 0); |
| |
878 info->use_major = FALSE; |
| |
879 */ |
| |
880 } |
| |
881 |
| |
882 void qq_process_recv_file_notify(guint8 *data, guint8 **cursor, gint data_len, |
| |
883 guint32 sender_uid, PurpleConnection *gc) |
| |
884 { |
| |
885 qq_data *qd; |
| |
886 ft_info *info; |
| |
887 PurpleXfer *xfer; |
| |
888 |
| |
889 g_return_if_fail (data != NULL && data_len != 0); |
| |
890 qd = (qq_data *) gc->proto_data; |
| |
891 |
| |
892 if (*cursor >= (data + data_len - 1)) { |
| |
893 purple_debug (PURPLE_DEBUG_WARNING, "QQ", |
| |
894 "Received file notify message is empty\n"); |
| |
895 return; |
| |
896 } |
| |
897 |
| |
898 xfer = qd->xfer; |
| |
899 info = (ft_info *) qd->xfer->data; |
| |
900 /* FIXME */ |
| |
901 read_packet_w(data, cursor, data_len, &(info->send_seq)); |
| |
902 |
| |
903 *cursor = data + 18 + 12; |
| |
904 qq_get_conn_info(data, cursor, data_len, info); |
| |
905 |
| |
906 _qq_xfer_init_udp_channel(info); |
| |
907 |
| |
908 xfer->watcher = purple_input_add(info->sender_fd, PURPLE_INPUT_WRITE, _qq_xfer_send_notify_ip_ack, xfer); |
| |
909 } |
| |
910 |
| |
911 /* temp placeholder until a working function can be implemented */ |
| |
912 gboolean qq_can_receive_file(PurpleConnection *gc, const char *who) |
| |
913 { |
| |
914 return TRUE; |
| |
915 } |
| |
916 |
| |
917 void qq_send_file(PurpleConnection *gc, const char *who, const char *file) |
| |
918 { |
| |
919 qq_data *qd; |
| |
920 PurpleXfer *xfer; |
| |
921 |
| |
922 qd = (qq_data *) gc->proto_data; |
| |
923 |
| |
924 xfer = purple_xfer_new (gc->account, PURPLE_XFER_SEND, |
| |
925 who); |
| |
926 if (xfer) |
| |
927 { |
| |
928 purple_xfer_set_init_fnc (xfer, _qq_xfer_init); |
| |
929 purple_xfer_set_cancel_send_fnc (xfer, _qq_xfer_cancel); |
| |
930 purple_xfer_set_write_fnc(xfer, _qq_xfer_write); |
| |
931 |
| |
932 qd->xfer = xfer; |
| |
933 purple_xfer_request(xfer); |
| |
934 } |
| |
935 } |
| |
936 |
| |
937 /* |
| |
938 static void qq_send_packet_request_key(PurpleConnection *gc, guint8 key) |
| |
939 { |
| |
940 qq_send_cmd(gc, QQ_CMD_REQUEST_KEY, TRUE, 0, TRUE, &key, 1); |
| |
941 } |
| |
942 |
| |
943 static void qq_process_recv_request_key(PurpleConnection *gc) |
| |
944 { |
| |
945 } |
| |
946 */ |