| |
1 /** |
| |
2 * The QQ2003C protocol plugin |
| |
3 * |
| |
4 * for gaim |
| |
5 * |
| |
6 * Author: Henry Ou <henry@linux.net> |
| |
7 * |
| |
8 * Copyright (C) 2004 Puzzlebird |
| |
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 gaim_debug(GAIM_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 = htons(info->remote_major_port); |
| |
71 sin.sin_addr.s_addr = htonl(info->remote_internet_ip); |
| |
72 } else { |
| |
73 sin.sin_port = htons(info->remote_minor_port); |
| |
74 sin.sin_addr.s_addr = 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, GaimXfer *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 gaim_debug(GAIM_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), ntohs(sin.sin_port)); |
| |
93 return r; |
| |
94 } |
| |
95 |
| |
96 /* |
| |
97 static ssize_t _qq_xfer_udp_send(const char *buf, size_t len, GaimXfer *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, GaimXfer *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 = htons(info->remote_major_port); |
| |
115 sin.sin_addr.s_addr = htonl(info->remote_internet_ip); |
| |
116 } else if (info->use_major) { |
| |
117 sin.sin_port = htons(info->remote_major_port); |
| |
118 sin.sin_addr.s_addr = htonl(info->remote_real_ip); |
| |
119 } else { |
| |
120 sin.sin_port = htons(info->remote_minor_port); |
| |
121 sin.sin_addr.s_addr = htonl(info->remote_real_ip); |
| |
122 } |
| |
123 gaim_debug(GAIM_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 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 gaim_xfer_read and gaim_xfer_write */ |
| |
134 |
| |
135 /* |
| |
136 static ssize_t _qq_xfer_read(char **buf, GaimXfer *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, GaimXfer *xfer) |
| |
144 { |
| |
145 return _qq_xfer_udp_send(buf, len, xfer); |
| |
146 } |
| |
147 |
| |
148 static void _qq_xfer_recv_packet(gpointer data, gint source, GaimInputCondition condition) |
| |
149 { |
| |
150 GaimXfer *xfer = (GaimXfer *) data; |
| |
151 GaimAccount *account = gaim_xfer_get_account(xfer); |
| |
152 GaimConnection *gc = gaim_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 static void _qq_xfer_send_start (GaimXfer *xfer) |
| |
170 { |
| |
171 GaimAccount *account; |
| |
172 GaimConnection *gc; |
| |
173 ft_info *info; |
| |
174 |
| |
175 account = gaim_xfer_get_account(xfer); |
| |
176 gc = gaim_account_get_connection(account); |
| |
177 info = (ft_info *) xfer->data; |
| |
178 } |
| |
179 |
| |
180 static void _qq_xfer_send_ack (GaimXfer *xfer, const char *buffer, size_t len) |
| |
181 { |
| |
182 GaimAccount *account; |
| |
183 GaimConnection *gc; |
| |
184 |
| |
185 account = gaim_xfer_get_account(xfer); |
| |
186 gc = gaim_account_get_connection(account); |
| |
187 qq_process_recv_file(gc, (guint8 *) buffer, len); |
| |
188 } |
| |
189 |
| |
190 static void _qq_xfer_recv_start(GaimXfer *xfer) |
| |
191 { |
| |
192 } |
| |
193 |
| |
194 static void _qq_xfer_end(GaimXfer *xfer) |
| |
195 { |
| |
196 ft_info *info; |
| |
197 g_return_if_fail(xfer != NULL && xfer->data != NULL); |
| |
198 info = (ft_info *) xfer->data; |
| |
199 |
| |
200 qq_xfer_close_file(xfer); |
| |
201 if (info->dest_fp != NULL) { |
| |
202 fclose(info->dest_fp); |
| |
203 gaim_debug(GAIM_DEBUG_INFO, "QQ", "file closed\n"); |
| |
204 } |
| |
205 if (info->major_fd != 0) { |
| |
206 close(info->major_fd); |
| |
207 gaim_debug(GAIM_DEBUG_INFO, "QQ", "major port closed\n"); |
| |
208 } |
| |
209 if (info->minor_fd != 0) { |
| |
210 close(info->minor_fd); |
| |
211 gaim_debug(GAIM_DEBUG_INFO, "QQ", "minor port closed\n"); |
| |
212 } |
| |
213 /* |
| |
214 if (info->buffer != NULL) { |
| |
215 munmap(info->buffer, gaim_xfer_get_size(xfer)); |
| |
216 gaim_debug(GAIM_DEBUG_INFO, "QQ", "file mapping buffer is freed.\n"); |
| |
217 } |
| |
218 */ |
| |
219 g_free(info); |
| |
220 } |
| |
221 |
| |
222 static void qq_show_conn_info(ft_info *info) |
| |
223 { |
| |
224 gchar *internet_ip_str, *real_ip_str; |
| |
225 guint32 ip; |
| |
226 |
| |
227 ip = htonl(info->remote_real_ip); |
| |
228 real_ip_str = gen_ip_str((guint8 *) &ip); |
| |
229 ip = htonl(info->remote_internet_ip); |
| |
230 internet_ip_str = gen_ip_str((guint8 *) &ip); |
| |
231 gaim_debug(GAIM_DEBUG_INFO, "QQ", "remote internet ip[%s:%d], major port[%d], real ip[%s], minor port[%d]\n", |
| |
232 internet_ip_str, info->remote_internet_port, |
| |
233 info->remote_major_port, real_ip_str, info->remote_minor_port |
| |
234 ); |
| |
235 g_free(real_ip_str); |
| |
236 g_free(internet_ip_str); |
| |
237 } |
| |
238 |
| |
239 void qq_get_conn_info(guint8 *data, guint8 **cursor, gint data_len, ft_info *info) |
| |
240 { |
| |
241 read_packet_data(data, cursor, data_len, info->file_session_key, 16); |
| |
242 *cursor += 30; |
| |
243 read_packet_b(data, cursor, data_len, &info->conn_method); |
| |
244 read_packet_dw(data, cursor, data_len, &info->remote_internet_ip); |
| |
245 read_packet_w(data, cursor, data_len, &info->remote_internet_port); |
| |
246 read_packet_w(data, cursor, data_len, &info->remote_major_port); |
| |
247 read_packet_dw(data, cursor, data_len, &info->remote_real_ip); |
| |
248 read_packet_w(data, cursor, data_len, &info->remote_minor_port); |
| |
249 qq_show_conn_info(info); |
| |
250 } |
| |
251 |
| |
252 gint qq_fill_conn_info(guint8 *raw_data, guint8 **cursor, ft_info *info) |
| |
253 { |
| |
254 gint bytes; |
| |
255 bytes = 0; |
| |
256 /* 064: connection method, UDP 0x00, TCP 0x03 */ |
| |
257 bytes += create_packet_b (raw_data, cursor, info->conn_method); |
| |
258 /* 065-068: outer ip address of sender (proxy address) */ |
| |
259 bytes += create_packet_dw (raw_data, cursor, info->local_internet_ip); |
| |
260 /* 069-070: sender port */ |
| |
261 bytes += create_packet_w (raw_data, cursor, info->local_internet_port); |
| |
262 /* 071-072: the first listening port(TCP doesn't have this part) */ |
| |
263 bytes += create_packet_w (raw_data, cursor, info->local_major_port); |
| |
264 /* 073-076: real ip */ |
| |
265 bytes += create_packet_dw (raw_data, cursor, info->local_real_ip); |
| |
266 /* 077-078: the second listening port */ |
| |
267 bytes += create_packet_w (raw_data, cursor, info->local_minor_port); |
| |
268 return bytes; |
| |
269 } |
| |
270 |
| |
271 |
| |
272 extern gchar *_gen_session_md5(gint uid, guint8 *session_key); |
| |
273 |
| |
274 /* fill in the common information of file transfer */ |
| |
275 static gint _qq_create_packet_file_header |
| |
276 (guint8 *raw_data, guint8 **cursor, guint32 to_uid, guint16 message_type, qq_data *qd, gboolean seq_ack) |
| |
277 { |
| |
278 gint bytes; |
| |
279 time_t now; |
| |
280 gchar *md5; |
| |
281 guint16 seq; |
| |
282 ft_info *info; |
| |
283 |
| |
284 bytes = 0; |
| |
285 now = time(NULL); |
| |
286 md5 = _gen_session_md5(qd->uid, qd->session_key); |
| |
287 if (!seq_ack) seq = qd->send_seq; |
| |
288 else { |
| |
289 info = (ft_info *) qd->xfer->data; |
| |
290 seq = info->send_seq; |
| |
291 } |
| |
292 |
| |
293 /* 000-003: receiver uid */ |
| |
294 bytes += create_packet_dw (raw_data, cursor, qd->uid); |
| |
295 /* 004-007: sender uid */ |
| |
296 bytes += create_packet_dw (raw_data, cursor, to_uid); |
| |
297 /* 008-009: sender client version */ |
| |
298 bytes += create_packet_w (raw_data, cursor, QQ_CLIENT); |
| |
299 /* 010-013: receiver uid */ |
| |
300 bytes += create_packet_dw (raw_data, cursor, qd->uid); |
| |
301 /* 014-017: sender uid */ |
| |
302 bytes += create_packet_dw (raw_data, cursor, to_uid); |
| |
303 /* 018-033: md5 of (uid+session_key) */ |
| |
304 bytes += create_packet_data (raw_data, cursor, md5, 16); |
| |
305 /* 034-035: message type */ |
| |
306 bytes += create_packet_w (raw_data, cursor, message_type); |
| |
307 /* 036-037: sequence number */ |
| |
308 bytes += create_packet_w (raw_data, cursor, seq); |
| |
309 /* 038-041: send time */ |
| |
310 bytes += create_packet_dw (raw_data, cursor, (guint32) now); |
| |
311 /* 042-042: always 0x00 */ |
| |
312 bytes += create_packet_b (raw_data, cursor, 0x00); |
| |
313 /* 043-043: sender icon */ |
| |
314 bytes += create_packet_b (raw_data, cursor, qd->my_icon); |
| |
315 /* 044-046: always 0x00 */ |
| |
316 bytes += create_packet_w (raw_data, cursor, 0x0000); |
| |
317 bytes += create_packet_b (raw_data, cursor, 0x00); |
| |
318 /* 047-047: we use font attr */ |
| |
319 bytes += create_packet_b (raw_data, cursor, 0x01); |
| |
320 /* 048-051: always 0x00 */ |
| |
321 bytes += create_packet_dw (raw_data, cursor, 0x00000000); |
| |
322 |
| |
323 /* 052-062: always 0x00 */ |
| |
324 bytes += create_packet_dw (raw_data, cursor, 0x00000000); |
| |
325 bytes += create_packet_dw (raw_data, cursor, 0x00000000); |
| |
326 bytes += create_packet_w (raw_data, cursor, 0x0000); |
| |
327 bytes += create_packet_b (raw_data, cursor, 0x00); |
| |
328 /* 063: transfer_type, 0x65: FILE 0x6b: FACE */ |
| |
329 bytes += create_packet_b (raw_data, cursor, QQ_FILE_TRANSFER_FILE); /* FIXME */ |
| |
330 |
| |
331 g_free (md5); |
| |
332 return bytes; |
| |
333 } |
| |
334 |
| |
335 #if 0 |
| |
336 in_addr_t get_real_ip() |
| |
337 { |
| |
338 char hostname[40]; |
| |
339 struct hostent *host; |
| |
340 |
| |
341 gethostname(hostname, sizeof(hostname)); |
| |
342 host = gethostbyname(hostname); |
| |
343 return *(host->h_addr); |
| |
344 } |
| |
345 |
| |
346 |
| |
347 #include <sys/ioctl.h> |
| |
348 #include <net/if.h> |
| |
349 |
| |
350 #define MAXINTERFACES 16 |
| |
351 in_addr_t get_real_ip() |
| |
352 { |
| |
353 int fd, intrface, i; |
| |
354 struct ifconf ifc; |
| |
355 struct ifreq buf[MAXINTERFACES]; |
| |
356 in_addr_t ret; |
| |
357 |
| |
358 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) return 0; |
| |
359 ifc.ifc_len = sizeof(buf); |
| |
360 ifc.ifc_buf = (caddr_t) buf; |
| |
361 if (ioctl(fd, SIOCGIFCONF, (char *) &ifc) < 0) return 0; |
| |
362 intrface = ifc.ifc_len / sizeof(struct ifreq); |
| |
363 for (i = 0; i < intrface; i++) { |
| |
364 //buf[intrface].ifr_name |
| |
365 if (ioctl(fd, SIOCGIFADDR, (char *) &buf[i]) >= 0) |
| |
366 { |
| |
367 ret = (((struct sockaddr_in *)(&buf[i].ifr_addr))->sin_addr).s_addr; |
| |
368 if (ret == ntohl(0x7f000001)) continue; |
| |
369 return ret; |
| |
370 } |
| |
371 } |
| |
372 return 0; |
| |
373 } |
| |
374 #endif |
| |
375 |
| |
376 static void _qq_xfer_init_socket(GaimXfer *xfer) |
| |
377 { |
| |
378 gint sockfd, listen_port = 0, i; |
| |
379 socklen_t sin_len; |
| |
380 struct sockaddr_in sin; |
| |
381 ft_info *info; |
| |
382 |
| |
383 g_return_if_fail(xfer != NULL); |
| |
384 g_return_if_fail(xfer->data != NULL); |
| |
385 info = (ft_info *) xfer->data; |
| |
386 |
| |
387 /* debug |
| |
388 info->local_real_ip = 0x7f000001; |
| |
389 */ |
| |
390 info->local_real_ip = ntohl(inet_addr(gaim_network_get_my_ip(-1))); |
| |
391 gaim_debug(GAIM_DEBUG_INFO, "QQ", "local real ip is %x", info->local_real_ip); |
| |
392 |
| |
393 for (i = 0; i < 2; i++) { |
| |
394 sockfd = socket(PF_INET, SOCK_DGRAM, 0); |
| |
395 g_return_if_fail(sockfd >= 0); |
| |
396 |
| |
397 memset(&sin, 0, sizeof(sin)); |
| |
398 sin.sin_family = AF_INET; |
| |
399 sin.sin_port = 0; |
| |
400 sin.sin_addr.s_addr = INADDR_ANY; |
| |
401 sin_len = sizeof(sin); |
| |
402 bind(sockfd, (struct sockaddr *) &sin, sin_len); |
| |
403 getsockname(sockfd, (struct sockaddr *) &sin, &sin_len); |
| |
404 listen_port = ntohs(sin.sin_port); |
| |
405 |
| |
406 switch (i) { |
| |
407 case 0: |
| |
408 info->local_major_port = listen_port; |
| |
409 info->major_fd = sockfd; |
| |
410 gaim_debug(GAIM_DEBUG_INFO, "QQ", "UDP Major Channel created on port[%d]\n", |
| |
411 info->local_major_port); |
| |
412 break; |
| |
413 case 1: |
| |
414 info->local_minor_port = listen_port; |
| |
415 info->minor_fd = sockfd; |
| |
416 gaim_debug(GAIM_DEBUG_INFO, "QQ", "UDP Minor Channel created on port[%d]\n", |
| |
417 info->local_minor_port); |
| |
418 break; |
| |
419 } |
| |
420 } |
| |
421 |
| |
422 if (_qq_in_same_lan(info)) { |
| |
423 info->sender_fd = info->recv_fd = info->minor_fd; |
| |
424 } else { |
| |
425 info->sender_fd = info->recv_fd = info->major_fd; |
| |
426 } |
| |
427 /* xfer->watcher = gaim_input_add(info->recv_fd, GAIM_INPUT_READ, _qq_xfer_recv_packet, xfer); */ |
| |
428 } |
| |
429 |
| |
430 /* create the QQ_FILE_TRANS_REQ packet with file infomations */ |
| |
431 static void _qq_send_packet_file_request (GaimConnection *gc, guint32 to_uid, gchar *filename, gint filesize) |
| |
432 { |
| |
433 qq_data *qd; |
| |
434 guint8 *cursor, *raw_data; |
| |
435 gchar *filelen_str; |
| |
436 gint filename_len, filelen_strlen, packet_len, bytes; |
| |
437 ft_info *info; |
| |
438 |
| |
439 qd = (qq_data *) gc->proto_data; |
| |
440 |
| |
441 info = g_new0(ft_info, 1); |
| |
442 info->to_uid = to_uid; |
| |
443 info->send_seq = qd->send_seq; |
| |
444 info->local_internet_ip = ntohl(inet_addr(qd->my_ip)); |
| |
445 info->local_internet_port = qd->my_port; |
| |
446 info->local_real_ip = 0x00000000; |
| |
447 info->conn_method = 0x00; |
| |
448 qd->xfer->data = info; |
| |
449 |
| |
450 filename_len = strlen(filename); |
| |
451 filelen_str = g_strdup_printf("%d ×Ö½Ú", filesize); |
| |
452 filelen_strlen = strlen(filelen_str); |
| |
453 |
| |
454 packet_len = 82 + filename_len + filelen_strlen; |
| |
455 raw_data = g_newa(guint8, packet_len); |
| |
456 cursor = raw_data; |
| |
457 |
| |
458 bytes = _qq_create_packet_file_header(raw_data, &cursor, to_uid, |
| |
459 QQ_FILE_TRANS_REQ, qd, FALSE); |
| |
460 bytes += qq_fill_conn_info(raw_data, &cursor, info); |
| |
461 /* 079: 0x20 */ |
| |
462 bytes += create_packet_b (raw_data, &cursor, 0x20); |
| |
463 /* 080: 0x1f */ |
| |
464 bytes += create_packet_b (raw_data, &cursor, 0x1f); |
| |
465 /* undetermined len: filename */ |
| |
466 bytes += create_packet_data (raw_data, &cursor, (guint8 *) filename, |
| |
467 filename_len); |
| |
468 /* 0x1f */ |
| |
469 bytes += create_packet_b (raw_data, &cursor, 0x1f); |
| |
470 /* file length */ |
| |
471 bytes += create_packet_data (raw_data, &cursor, (guint8 *) filelen_str, |
| |
472 filelen_strlen); |
| |
473 |
| |
474 if (packet_len == bytes) |
| |
475 qq_send_cmd (gc, QQ_CMD_SEND_IM, TRUE, 0, TRUE, raw_data, |
| |
476 cursor - raw_data); |
| |
477 else |
| |
478 gaim_debug (GAIM_DEBUG_INFO, "qq_send_packet_file_request", |
| |
479 "%d bytes expected but got %d bytes\n", |
| |
480 packet_len, bytes); |
| |
481 |
| |
482 g_free (filelen_str); |
| |
483 } |
| |
484 |
| |
485 /* tell the buddy we want to accept the file */ |
| |
486 static void _qq_send_packet_file_accept(GaimConnection *gc, guint32 to_uid) |
| |
487 { |
| |
488 qq_data *qd; |
| |
489 guint8 *cursor, *raw_data; |
| |
490 guint16 minor_port; |
| |
491 guint32 real_ip; |
| |
492 gint packet_len, bytes; |
| |
493 ft_info *info; |
| |
494 |
| |
495 g_return_if_fail (gc != NULL && gc->proto_data != NULL); |
| |
496 qd = (qq_data *) gc->proto_data; |
| |
497 info = (ft_info *) qd->xfer->data; |
| |
498 |
| |
499 gaim_debug(GAIM_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 gaim_debug (GAIM_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(GaimConnection *gc, guint32 to_uid) |
| |
527 { |
| |
528 GaimXfer *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 gaim_debug(GAIM_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 gaim_debug (GAIM_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) gaim_input_remove(xfer->watcher); |
| |
554 xfer->watcher = gaim_input_add(info->recv_fd, GAIM_INPUT_READ, _qq_xfer_recv_packet, xfer); |
| |
555 gaim_input_add(info->major_fd, GAIM_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 (GaimConnection *gc, guint32 to_uid) |
| |
560 { |
| |
561 qq_data *qd; |
| |
562 guint8 *cursor, *raw_data; |
| |
563 gint packet_len, bytes; |
| |
564 |
| |
565 gaim_debug(GAIM_DEBUG_INFO, "_qq_send_packet_file_reject", "start"); |
| |
566 g_return_if_fail(gc != NULL && gc->proto_data != NULL); |
| |
567 qd = (qq_data *) gc->proto_data; |
| |
568 |
| |
569 packet_len = 64; |
| |
570 raw_data = g_newa (guint8, packet_len); |
| |
571 cursor = raw_data; |
| |
572 bytes = 0; |
| |
573 |
| |
574 bytes = _qq_create_packet_file_header(raw_data, &cursor, to_uid, QQ_FILE_TRANS_DENY_UDP, qd, TRUE); |
| |
575 |
| |
576 if (packet_len == bytes) |
| |
577 qq_send_cmd (gc, QQ_CMD_SEND_IM, TRUE, 0, TRUE, raw_data, |
| |
578 cursor - raw_data); |
| |
579 else |
| |
580 gaim_debug (GAIM_DEBUG_INFO, "qq_send_packet_file", |
| |
581 "%d bytes expected but got %d bytes\n", |
| |
582 packet_len, bytes); |
| |
583 } |
| |
584 |
| |
585 /* tell the buddy to cancel transfer */ |
| |
586 static void _qq_send_packet_file_cancel (GaimConnection *gc, guint32 to_uid) |
| |
587 { |
| |
588 qq_data *qd; |
| |
589 guint8 *cursor, *raw_data; |
| |
590 gint packet_len, bytes; |
| |
591 |
| |
592 gaim_debug(GAIM_DEBUG_INFO, "_qq_send_packet_file_cancel", "start\n"); |
| |
593 g_return_if_fail(gc != NULL && gc->proto_data != NULL); |
| |
594 qd = (qq_data *) gc->proto_data; |
| |
595 |
| |
596 packet_len = 64; |
| |
597 raw_data = g_newa (guint8, packet_len); |
| |
598 cursor = raw_data; |
| |
599 bytes = 0; |
| |
600 |
| |
601 gaim_debug(GAIM_DEBUG_INFO, "_qq_send_packet_file_cancel", "before create header\n"); |
| |
602 bytes = _qq_create_packet_file_header(raw_data, &cursor, to_uid, QQ_FILE_TRANS_CANCEL, qd, TRUE); |
| |
603 gaim_debug(GAIM_DEBUG_INFO, "_qq_send_packet_file_cancel", "end create header\n"); |
| |
604 |
| |
605 if (packet_len == bytes) { |
| |
606 gaim_debug(GAIM_DEBUG_INFO, "_qq_send_packet_file_cancel", "before send cmd\n"); |
| |
607 qq_send_cmd (gc, QQ_CMD_SEND_IM, TRUE, 0, TRUE, raw_data, |
| |
608 cursor - raw_data); |
| |
609 } |
| |
610 else |
| |
611 gaim_debug (GAIM_DEBUG_INFO, "qq_send_packet_file", |
| |
612 "%d bytes expected but got %d bytes\n", |
| |
613 packet_len, bytes); |
| |
614 |
| |
615 gaim_debug (GAIM_DEBUG_INFO, "qq_send_packet_file_cancel", "end\n"); |
| |
616 } |
| |
617 |
| |
618 /* request to send a file */ |
| |
619 static void |
| |
620 _qq_xfer_init (GaimXfer * xfer) |
| |
621 { |
| |
622 GaimConnection *gc; |
| |
623 GaimAccount *account; |
| |
624 guint32 to_uid; |
| |
625 gchar *filename, *filename_without_path; |
| |
626 |
| |
627 g_return_if_fail (xfer != NULL); |
| |
628 account = gaim_xfer_get_account(xfer); |
| |
629 gc = gaim_account_get_connection(account); |
| |
630 g_return_if_fail (gc != NULL && gc->proto_data != NULL); |
| |
631 |
| |
632 to_uid = gaim_name_to_uid (xfer->who); |
| |
633 g_return_if_fail (to_uid != 0); |
| |
634 |
| |
635 filename = (gchar *) gaim_xfer_get_local_filename (xfer); |
| |
636 g_return_if_fail (filename != NULL); |
| |
637 |
| |
638 filename_without_path = strrchr (filename, '/') + 1; |
| |
639 |
| |
640 _qq_send_packet_file_request (gc, to_uid, filename_without_path, |
| |
641 gaim_xfer_get_size(xfer)); |
| |
642 } |
| |
643 |
| |
644 /* cancel the transfer of receiving files */ |
| |
645 static void _qq_xfer_cancel(GaimXfer *xfer) |
| |
646 { |
| |
647 GaimConnection *gc; |
| |
648 GaimAccount *account; |
| |
649 guint16 *seq; |
| |
650 |
| |
651 g_return_if_fail (xfer != NULL); |
| |
652 seq = (guint16 *) xfer->data; |
| |
653 account = gaim_xfer_get_account(xfer); |
| |
654 gc = gaim_account_get_connection(account); |
| |
655 |
| |
656 switch (gaim_xfer_get_status(xfer)) { |
| |
657 case GAIM_XFER_STATUS_CANCEL_LOCAL: |
| |
658 _qq_send_packet_file_cancel(gc, gaim_name_to_uid(xfer->who)); |
| |
659 break; |
| |
660 case GAIM_XFER_STATUS_CANCEL_REMOTE: |
| |
661 _qq_send_packet_file_cancel(gc, gaim_name_to_uid(xfer->who)); |
| |
662 break; |
| |
663 case GAIM_XFER_STATUS_NOT_STARTED: |
| |
664 break; |
| |
665 case GAIM_XFER_STATUS_UNKNOWN: |
| |
666 _qq_send_packet_file_reject(gc, gaim_name_to_uid(xfer->who)); |
| |
667 break; |
| |
668 case GAIM_XFER_STATUS_DONE: |
| |
669 break; |
| |
670 case GAIM_XFER_STATUS_ACCEPTED: |
| |
671 break; |
| |
672 case GAIM_XFER_STATUS_STARTED: |
| |
673 break; |
| |
674 } |
| |
675 } |
| |
676 |
| |
677 /* init the transfer of receiving files */ |
| |
678 static void _qq_xfer_recv_init(GaimXfer *xfer) |
| |
679 { |
| |
680 GaimConnection *gc; |
| |
681 GaimAccount *account; |
| |
682 ft_info *info; |
| |
683 |
| |
684 g_return_if_fail (xfer != NULL && xfer->data != NULL); |
| |
685 info = (ft_info *) xfer->data; |
| |
686 account = gaim_xfer_get_account(xfer); |
| |
687 gc = gaim_account_get_connection(account); |
| |
688 |
| |
689 _qq_send_packet_file_accept(gc, gaim_name_to_uid(xfer->who)); |
| |
690 } |
| |
691 |
| |
692 /* process reject im for file transfer request */ |
| |
693 void qq_process_recv_file_reject (guint8 *data, guint8 **cursor, gint data_len, |
| |
694 guint32 sender_uid, GaimConnection *gc) |
| |
695 { |
| |
696 gchar *msg, *filename; |
| |
697 qq_data *qd; |
| |
698 |
| |
699 g_return_if_fail (gc != NULL && data != NULL && data_len != 0); |
| |
700 qd = (qq_data *) gc->proto_data; |
| |
701 g_return_if_fail (qd != NULL && qd->xfer != NULL); |
| |
702 |
| |
703 if (*cursor >= (data + data_len - 1)) { |
| |
704 gaim_debug (GAIM_DEBUG_WARNING, "QQ", |
| |
705 "Received file reject message is empty\n"); |
| |
706 return; |
| |
707 } |
| |
708 filename = strrchr(gaim_xfer_get_local_filename(qd->xfer), '/') + 1; |
| |
709 msg = g_strdup_printf |
| |
710 (_ |
| |
711 ("Your request to send file[%s] has been rejected by buddy[%d]"), |
| |
712 filename, sender_uid); |
| |
713 |
| |
714 gaim_notify_warning (gc, _("File Send"), msg, NULL); |
| |
715 gaim_xfer_request_denied(qd->xfer); |
| |
716 qd->xfer = NULL; |
| |
717 |
| |
718 g_free (msg); |
| |
719 } |
| |
720 |
| |
721 /* process cancel im for file transfer request */ |
| |
722 void qq_process_recv_file_cancel (guint8 *data, guint8 **cursor, gint data_len, |
| |
723 guint32 sender_uid, GaimConnection * gc) |
| |
724 { |
| |
725 gchar *msg, *filename; |
| |
726 qq_data *qd; |
| |
727 |
| |
728 g_return_if_fail (gc != NULL && data != NULL && data_len != 0); |
| |
729 qd = (qq_data *) gc->proto_data; |
| |
730 g_return_if_fail (qd != NULL && qd->xfer != NULL |
| |
731 && gaim_xfer_get_filename(qd->xfer) != NULL); |
| |
732 |
| |
733 if (*cursor >= (data + data_len - 1)) { |
| |
734 gaim_debug (GAIM_DEBUG_WARNING, "QQ", |
| |
735 "Received file reject message is empty\n"); |
| |
736 return; |
| |
737 } |
| |
738 filename = strrchr(gaim_xfer_get_local_filename(qd->xfer), '/') + 1; |
| |
739 msg = g_strdup_printf |
| |
740 (_("The sending process of file[%s] has been cancaled by buddy[%d]"), |
| |
741 filename, sender_uid); |
| |
742 |
| |
743 gaim_notify_warning (gc, _("File Send"), msg, NULL); |
| |
744 gaim_xfer_cancel_remote(qd->xfer); |
| |
745 qd->xfer = NULL; |
| |
746 |
| |
747 g_free (msg); |
| |
748 } |
| |
749 |
| |
750 /* process accept im for file transfer request */ |
| |
751 void qq_process_recv_file_accept(guint8 *data, guint8 **cursor, gint data_len, |
| |
752 guint32 sender_uid, GaimConnection * gc) |
| |
753 { |
| |
754 qq_data *qd; |
| |
755 ft_info *info; |
| |
756 GaimXfer *xfer; |
| |
757 |
| |
758 g_return_if_fail (gc != NULL && data != NULL && data_len != 0); |
| |
759 qd = (qq_data *) gc->proto_data; |
| |
760 xfer = qd->xfer; |
| |
761 |
| |
762 if (*cursor >= (data + data_len - 1)) { |
| |
763 gaim_debug (GAIM_DEBUG_WARNING, "QQ", |
| |
764 "Received file reject message is empty\n"); |
| |
765 return; |
| |
766 } |
| |
767 |
| |
768 info = (ft_info *) qd->xfer->data; |
| |
769 |
| |
770 *cursor = data + 18 + 12; |
| |
771 qq_get_conn_info(data, cursor, data_len, info); |
| |
772 _qq_xfer_init_socket(qd->xfer); |
| |
773 |
| |
774 _qq_xfer_init_udp_channel(info); |
| |
775 _qq_send_packet_file_notifyip(gc, sender_uid); |
| |
776 } |
| |
777 |
| |
778 /* process request from buddy's im for file transfer request */ |
| |
779 void qq_process_recv_file_request(guint8 *data, guint8 **cursor, gint data_len, |
| |
780 guint32 sender_uid, GaimConnection * gc) |
| |
781 { |
| |
782 qq_data *qd; |
| |
783 GaimXfer *xfer; |
| |
784 gchar *sender_name, **fileinfo; |
| |
785 ft_info *info; |
| |
786 GaimBuddy *b; |
| |
787 qq_buddy *q_bud; |
| |
788 |
| |
789 g_return_if_fail (gc != NULL && data != NULL && data_len != 0); |
| |
790 qd = (qq_data *) gc->proto_data; |
| |
791 |
| |
792 if (*cursor >= (data + data_len - 1)) { |
| |
793 gaim_debug (GAIM_DEBUG_WARNING, "QQ", |
| |
794 "Received file reject message is empty\n"); |
| |
795 return; |
| |
796 } |
| |
797 |
| |
798 info = g_new0(ft_info, 1); |
| |
799 info->local_internet_ip = ntohl(inet_addr(qd->my_ip)); |
| |
800 info->local_internet_port = qd->my_port; |
| |
801 info->local_real_ip = 0x00000000; |
| |
802 info->to_uid = sender_uid; |
| |
803 read_packet_w(data, cursor, data_len, &(info->send_seq)); |
| |
804 |
| |
805 *cursor = data + 18 + 12; |
| |
806 qq_get_conn_info(data, cursor, data_len, info); |
| |
807 |
| |
808 fileinfo = g_strsplit((gchar *) (data + 81 + 12), "\x1f", 2); |
| |
809 g_return_if_fail (fileinfo != NULL && fileinfo[0] != NULL && fileinfo[1] != NULL); |
| |
810 |
| |
811 sender_name = uid_to_gaim_name(sender_uid); |
| |
812 |
| |
813 /* FACE from IP detector, ignored by gfhuang */ |
| |
814 if(g_ascii_strcasecmp(fileinfo[0], "FACE") == 0) { |
| |
815 gaim_debug(GAIM_DEBUG_WARNING, "QQ", |
| |
816 "Received a FACE ip detect from qq-%d, so he/she must be online :)\n", sender_uid); |
| |
817 |
| |
818 b = gaim_find_buddy(gc->account, sender_name); |
| |
819 q_bud = (b == NULL) ? NULL : (qq_buddy *) b->proto_data; |
| |
820 if (q_bud) { |
| |
821 if(0 != info->remote_real_ip) { |
| |
822 g_memmove(q_bud->ip, &info->remote_real_ip, 4); |
| |
823 q_bud->port = info->remote_minor_port; |
| |
824 } |
| |
825 else if (0 != info->remote_internet_ip) { |
| |
826 g_memmove(q_bud->ip, &info->remote_internet_ip, 4); |
| |
827 q_bud->port = info->remote_major_port; |
| |
828 } |
| |
829 |
| |
830 if(!is_online(q_bud->status)) { |
| |
831 q_bud->status = QQ_BUDDY_ONLINE_INVISIBLE; |
| |
832 qq_update_buddy_contact(gc, q_bud); |
| |
833 } |
| |
834 else |
| |
835 gaim_debug(GAIM_DEBUG_INFO, "QQ", "buddy %d is already online\n", sender_uid); |
| |
836 |
| |
837 } |
| |
838 else |
| |
839 gaim_debug(GAIM_DEBUG_WARNING, "QQ", "buddy %d is not in my friendlist\n", sender_uid); |
| |
840 |
| |
841 g_free(sender_name); |
| |
842 g_strfreev(fileinfo); |
| |
843 return; |
| |
844 } |
| |
845 |
| |
846 xfer = gaim_xfer_new(gaim_connection_get_account(gc), |
| |
847 GAIM_XFER_RECEIVE, |
| |
848 sender_name); |
| |
849 gaim_xfer_set_filename(xfer, fileinfo[0]); |
| |
850 gaim_xfer_set_size(xfer, atoi(fileinfo[1])); |
| |
851 |
| |
852 gaim_xfer_set_init_fnc(xfer, _qq_xfer_recv_init); |
| |
853 gaim_xfer_set_request_denied_fnc(xfer, _qq_xfer_cancel); |
| |
854 gaim_xfer_set_cancel_recv_fnc(xfer, _qq_xfer_cancel); |
| |
855 gaim_xfer_set_end_fnc(xfer, _qq_xfer_end); |
| |
856 gaim_xfer_set_write_fnc(xfer, _qq_xfer_write); |
| |
857 |
| |
858 xfer->data = info; |
| |
859 qd->xfer = xfer; |
| |
860 |
| |
861 gaim_xfer_request(xfer); |
| |
862 |
| |
863 g_free(sender_name); |
| |
864 g_strfreev(fileinfo); |
| |
865 } |
| |
866 |
| |
867 static void _qq_xfer_send_notify_ip_ack(gpointer data, gint source, GaimInputCondition cond) |
| |
868 { |
| |
869 GaimXfer *xfer = (GaimXfer *) data; |
| |
870 GaimAccount *account = gaim_xfer_get_account(xfer); |
| |
871 GaimConnection *gc = gaim_account_get_connection(account); |
| |
872 ft_info *info = (ft_info *) xfer->data; |
| |
873 |
| |
874 gaim_input_remove(xfer->watcher); |
| |
875 xfer->watcher = gaim_input_add(info->recv_fd, GAIM_INPUT_READ, _qq_xfer_recv_packet, xfer); |
| |
876 qq_send_file_ctl_packet(gc, QQ_FILE_CMD_NOTIFY_IP_ACK, info->to_uid, 0); |
| |
877 /* |
| |
878 info->use_major = TRUE; |
| |
879 qq_send_file_ctl_packet(gc, QQ_FILE_CMD_NOTIFY_IP_ACK, info->to_uid, 0); |
| |
880 info->use_major = FALSE; |
| |
881 */ |
| |
882 } |
| |
883 |
| |
884 void qq_process_recv_file_notify(guint8 *data, guint8 **cursor, gint data_len, |
| |
885 guint32 sender_uid, GaimConnection *gc) |
| |
886 { |
| |
887 qq_data *qd; |
| |
888 ft_info *info; |
| |
889 GaimXfer *xfer; |
| |
890 |
| |
891 g_return_if_fail (gc != NULL && data != NULL && data_len != 0); |
| |
892 qd = (qq_data *) gc->proto_data; |
| |
893 |
| |
894 if (*cursor >= (data + data_len - 1)) { |
| |
895 gaim_debug (GAIM_DEBUG_WARNING, "QQ", |
| |
896 "Received file notify message is empty\n"); |
| |
897 return; |
| |
898 } |
| |
899 |
| |
900 xfer = qd->xfer; |
| |
901 info = (ft_info *) qd->xfer->data; |
| |
902 /* FIXME */ |
| |
903 read_packet_w(data, cursor, data_len, &(info->send_seq)); |
| |
904 |
| |
905 *cursor = data + 18 + 12; |
| |
906 qq_get_conn_info(data, cursor, data_len, info); |
| |
907 |
| |
908 _qq_xfer_init_udp_channel(info); |
| |
909 |
| |
910 xfer->watcher = gaim_input_add(info->sender_fd, GAIM_INPUT_WRITE, _qq_xfer_send_notify_ip_ack, xfer); |
| |
911 } |
| |
912 |
| |
913 /* temp placeholder until a working function can be implemented */ |
| |
914 gboolean qq_can_receive_file(GaimConnection *gc, const char *who) |
| |
915 { |
| |
916 return TRUE; |
| |
917 } |
| |
918 |
| |
919 void qq_send_file(GaimConnection *gc, const char *who, const char *file) |
| |
920 { |
| |
921 qq_data *qd; |
| |
922 GaimXfer *xfer; |
| |
923 |
| |
924 g_return_if_fail (gc != NULL && gc->proto_data != NULL); |
| |
925 qd = (qq_data *) gc->proto_data; |
| |
926 |
| |
927 xfer = gaim_xfer_new (gc->account, GAIM_XFER_SEND, |
| |
928 who); |
| |
929 gaim_xfer_set_init_fnc (xfer, _qq_xfer_init); |
| |
930 gaim_xfer_set_cancel_send_fnc (xfer, _qq_xfer_cancel); |
| |
931 gaim_xfer_set_write_fnc(xfer, _qq_xfer_write); |
| |
932 |
| |
933 qd->xfer = xfer; |
| |
934 gaim_xfer_request (xfer); |
| |
935 } |
| |
936 |
| |
937 static void qq_send_packet_request_key(GaimConnection *gc, guint8 key) |
| |
938 { |
| |
939 qq_send_cmd(gc, QQ_CMD_REQUEST_KEY, TRUE, 0, TRUE, &key, 1); |
| |
940 } |
| |
941 |
| |
942 static void qq_process_recv_request_key(GaimConnection *gc) |
| |
943 { |
| |
944 } |