comparison libpurple/protocols/qq/file_trans.c @ 15374:5fe8042783c1

Rename gtk/ and libgaim/ to pidgin/ and libpurple/
author Sean Egan <seanegan@gmail.com>
date Sat, 20 Jan 2007 02:32:10 +0000
parents
children 32c366eeeb99
comparison
equal deleted inserted replaced
15373:f79e0f4df793 15374:5fe8042783c1
1 /**
2 * @file file_trans.c
3 *
4 * gaim
5 *
6 * Gaim 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 #ifdef _WIN32
26 #define random rand
27 #endif
28
29 #include "debug.h"
30 #include "ft.h"
31 #include "cipher.h"
32
33 #include "crypt.h"
34 #include "file_trans.h"
35 #include "header_info.h"
36 #include "im.h"
37 #include "packet_parse.h"
38 #include "proxy.h"
39 #include "send_core.h"
40 #include "send_file.h"
41 #include "utils.h"
42
43 struct _qq_file_header {
44 guint8 tag;
45 guint16 client_ver;
46 guint8 file_key;
47 guint32 sender_uid;
48 guint32 receiver_uid;
49 };
50
51 typedef struct _qq_file_header qq_file_header;
52
53 static guint32 _get_file_key(guint8 seed)
54 {
55 guint32 key;
56 key = seed | (seed << 8) | (seed << 16) | (seed << 24);
57 return key;
58 }
59
60 static guint32 _gen_file_key()
61 {
62 guint8 seed;
63
64 seed = random();
65 return _get_file_key(seed);
66 }
67
68 static guint32 _decrypt_qq_uid(guint32 uid, guint32 key)
69 {
70 return ~(uid ^ key);
71 }
72
73 static guint32 _encrypt_qq_uid(guint32 uid, guint32 key)
74 {
75 return (~uid) ^ key;
76 }
77
78 static void _fill_filename_md5(const gchar *filename, guint8 *md5)
79 {
80 GaimCipher *cipher;
81 GaimCipherContext *context;
82
83 g_return_if_fail(filename != NULL && md5 != NULL);
84
85 cipher = gaim_ciphers_find_cipher("md5");
86 context = gaim_cipher_context_new(cipher, NULL);
87 gaim_cipher_context_append(context, (guint8 *) filename, strlen(filename));
88 gaim_cipher_context_digest(context, 16, md5, NULL);
89 gaim_cipher_context_destroy(context);
90 }
91
92 static void _fill_file_md5(const gchar *filename, gint filelen, guint8 *md5)
93 {
94 FILE *fp;
95 guint8 *buffer;
96 GaimCipher *cipher;
97 GaimCipherContext *context;
98
99 const gint QQ_MAX_FILE_MD5_LENGTH = 10002432;
100
101 g_return_if_fail(filename != NULL && md5 != NULL);
102 if (filelen > QQ_MAX_FILE_MD5_LENGTH)
103 filelen = QQ_MAX_FILE_MD5_LENGTH;
104
105 fp = fopen(filename, "rb");
106 g_return_if_fail(fp != NULL);
107
108 buffer = g_newa(guint8, filelen);
109 g_return_if_fail(buffer != NULL);
110 fread(buffer, filelen, 1, fp);
111
112 cipher = gaim_ciphers_find_cipher("md5");
113 context = gaim_cipher_context_new(cipher, NULL);
114 gaim_cipher_context_append(context, buffer, filelen);
115 gaim_cipher_context_digest(context, 16, md5, NULL);
116 gaim_cipher_context_destroy(context);
117
118 fclose(fp);
119 }
120
121 static void _qq_get_file_header(guint8 *buf, guint8 **cursor, gint buflen, qq_file_header *fh)
122 {
123 read_packet_b(buf, cursor, buflen, &(fh->tag));
124 read_packet_w(buf, cursor, buflen, &(fh->client_ver));
125 read_packet_b(buf, cursor, buflen, &fh->file_key);
126 read_packet_dw(buf, cursor, buflen, &(fh->sender_uid));
127 read_packet_dw(buf, cursor, buflen, &(fh->receiver_uid));
128
129 fh->sender_uid = _decrypt_qq_uid(fh->sender_uid, _get_file_key(fh->file_key));
130 fh->receiver_uid = _decrypt_qq_uid(fh->receiver_uid, _get_file_key(fh->file_key));
131 }
132
133 static const gchar *qq_get_file_cmd_desc(gint type)
134 {
135 switch (type) {
136 case QQ_FILE_CMD_SENDER_SAY_HELLO:
137 return "QQ_FILE_CMD_SENDER_SAY_HELLO";
138 case QQ_FILE_CMD_SENDER_SAY_HELLO_ACK:
139 return "QQ_FILE_CMD_SENDER_SAY_HELLO_ACK";
140 case QQ_FILE_CMD_RECEIVER_SAY_HELLO:
141 return "QQ_FILE_CMD_RECEIVER_SAY_HELLO";
142 case QQ_FILE_CMD_RECEIVER_SAY_HELLO_ACK:
143 return "QQ_FILE_CMD_RECEIVER_SAY_HELLO_ACK";
144 case QQ_FILE_CMD_NOTIFY_IP_ACK:
145 return "QQ_FILE_CMD_NOTIFY_IP_ACK";
146 case QQ_FILE_CMD_PING:
147 return "QQ_FILE_CMD_PING";
148 case QQ_FILE_CMD_PONG:
149 return "QQ_FILE_CMD_PONG";
150 case QQ_FILE_CMD_INITATIVE_CONNECT:
151 return "QQ_FILE_CMD_INITATIVE_CONNECT";
152 case QQ_FILE_CMD_FILE_OP:
153 return "QQ_FILE_CMD_FILE_OP";
154 case QQ_FILE_CMD_FILE_OP_ACK:
155 return "QQ_FILE_CMD_FILE_OP_ACK";
156 case QQ_FILE_BASIC_INFO:
157 return "QQ_FILE_BASIC_INFO";
158 case QQ_FILE_DATA_INFO:
159 return "QQ_FILE_DATA_INFO";
160 case QQ_FILE_EOF:
161 return "QQ_FILE_EOF";
162 default:
163 return "UNKNOWN_TYPE";
164 }
165 }
166
167 /* The memmap version has better performance for big files transfering
168 * but it will spend plenty of memory, so do not use it in a low-memory host */
169 #ifdef USE_MMAP
170 #include <sys/mman.h>
171
172 static int _qq_xfer_open_file(const gchar *filename, const gchar *method, GaimXfer *xfer)
173 {
174 ft_info *info = xfer->data;
175 int fd;
176 if (method[0] == 'r') {
177 fd = open(gaim_xfer_get_local_filename(xfer), O_RDONLY);
178 info->buffer = mmap(0, gaim_xfer_get_size(xfer), PROT_READ, MAP_PRIVATE, fd, 0);
179 }
180 else
181 {
182 fd = open(gaim_xfer_get_local_filename(xfer), O_RDWR|O_CREAT, 0644);
183 info->buffer = mmap(0, gaim_xfer_get_size(xfer), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FILE, fd, 0);
184 }
185
186 if (info->buffer == NULL) {
187 return - 1;
188 }
189 return 0;
190 }
191
192 static gint _qq_xfer_read_file(guint8 *buffer, guint index, guint len, GaimXfer *xfer)
193 {
194 ft_info *info = xfer->data;
195 gint readbytes;
196
197 buffer = info->buffer + len * index;
198 readbytes = gaim_xfer_get_size(xfer) - (buffer - info->buffer);
199 if (readbytes > info->fragment_len) readbytes = info->fragment_len;
200 return readbytes;
201 }
202
203 static gint _qq_xfer_write_file(guint8 *buffer, guint index, guint len, GaimXfer *xfer)
204 {
205 ft_info *info = xfer->data;
206
207 memcpy(info->buffer + index * len, buffer, len);
208 return 0;
209 }
210
211 void qq_xfer_close_file(GaimXfer *xfer)
212 {
213 ft_info *info = xfer->data;
214
215 if (info->buffer) munmap(info->buffer, gaim_xfer_get_size(xfer));
216 }
217 #else
218 static int _qq_xfer_open_file(const gchar *filename, const gchar *method, GaimXfer *xfer)
219 {
220 ft_info *info = xfer->data;
221 info->dest_fp = fopen(gaim_xfer_get_local_filename(xfer), method);
222 if (info->dest_fp == NULL) {
223 return -1;
224 }
225 return 0;
226 }
227
228 static gint _qq_xfer_read_file(guint8 *buffer, guint index, guint len, GaimXfer *xfer)
229 {
230 ft_info *info = xfer->data;
231
232 fseek(info->dest_fp, index * len, SEEK_SET);
233 return fread(buffer, 1, len, info->dest_fp);
234 }
235
236 static gint _qq_xfer_write_file(guint8 *buffer, guint index, guint len, GaimXfer *xfer)
237 {
238 ft_info *info = xfer->data;
239 fseek(info->dest_fp, index * len, SEEK_SET);
240 return fwrite(buffer, 1, len, info->dest_fp);
241 }
242
243 void qq_xfer_close_file(GaimXfer *xfer)
244 {
245 ft_info *info = xfer->data;
246
247 if (info->dest_fp) fclose(info->dest_fp);
248 }
249 #endif
250
251 static gint _qq_send_file(GaimConnection *gc, guint8 *data, gint len, guint16 packet_type, guint32 to_uid)
252 {
253 gint bytes;
254 guint8 *cursor, *buf;
255 guint32 file_key;
256 qq_data *qd;
257 ft_info *info;
258
259 qd = (qq_data *) gc->proto_data;
260 g_return_val_if_fail(qd->session_key != NULL, -1);
261 info = (ft_info *) qd->xfer->data;
262 bytes = 0;
263
264 buf = g_newa(guint8, MAX_PACKET_SIZE);
265 cursor = buf;
266 file_key = _gen_file_key();
267
268 bytes += create_packet_b(buf, &cursor, packet_type);
269 bytes += create_packet_w(buf, &cursor, QQ_CLIENT);
270 bytes += create_packet_b(buf, &cursor, file_key & 0xff);
271 bytes += create_packet_dw(buf, &cursor, _encrypt_qq_uid(qd->uid, file_key));
272 bytes += create_packet_dw(buf, &cursor, _encrypt_qq_uid(to_uid, file_key));
273 bytes += create_packet_data(buf, &cursor, data, len);
274
275 if (bytes == len + 12) {
276 _qq_xfer_write(buf, bytes, qd->xfer);
277 } else
278 gaim_debug(GAIM_DEBUG_INFO, "QQ", "send_file: want %d but got %d\n", len + 12, bytes);
279 return bytes;
280 }
281
282 /* send a file to udp channel with QQ_FILE_CONTROL_PACKET_TAG */
283 void qq_send_file_ctl_packet(GaimConnection *gc, guint16 packet_type, guint32 to_uid, guint8 hellobyte)
284 {
285 qq_data *qd;
286 gint bytes, bytes_expected, encrypted_len;
287 guint8 *raw_data, *cursor, *encrypted_data;
288 time_t now;
289 ft_info *info;
290
291 qd = (qq_data *) gc->proto_data;
292 info = (ft_info *) qd->xfer->data;
293
294 raw_data = g_new0 (guint8, 61);
295 cursor = raw_data;
296
297 bytes = 0;
298 now = time(NULL);
299
300 bytes += create_packet_data(raw_data, &cursor, qd->session_md5, 16);
301 bytes += create_packet_w(raw_data, &cursor, packet_type);
302 switch (packet_type) {
303 case QQ_FILE_CMD_SENDER_SAY_HELLO:
304 case QQ_FILE_CMD_SENDER_SAY_HELLO_ACK:
305 case QQ_FILE_CMD_RECEIVER_SAY_HELLO_ACK:
306 case QQ_FILE_CMD_NOTIFY_IP_ACK:
307 case QQ_FILE_CMD_RECEIVER_SAY_HELLO:
308 bytes += create_packet_w(raw_data, &cursor, info->send_seq);
309 break;
310 default:
311 bytes += create_packet_w(raw_data, &cursor, ++qd->send_seq);
312 }
313 bytes += create_packet_dw(raw_data, &cursor, (guint32) now);
314 bytes += create_packet_b(raw_data, &cursor, 0x00);
315 bytes += create_packet_b(raw_data, &cursor, qd->my_icon);
316 bytes += create_packet_dw(raw_data, &cursor, 0x00000000);
317 bytes += create_packet_dw(raw_data, &cursor, 0x00000000);
318 bytes += create_packet_dw(raw_data, &cursor, 0x00000000);
319 bytes += create_packet_dw(raw_data, &cursor, 0x00000000);
320 bytes += create_packet_w(raw_data, &cursor, 0x0000);
321 bytes += create_packet_b(raw_data, &cursor, 0x00);
322 /* 0x65: send a file, 0x6b: send a custom face */
323 bytes += create_packet_b(raw_data, &cursor, QQ_FILE_TRANSFER_FILE); /* FIXME temp by gfhuang */
324 switch (packet_type)
325 {
326 case QQ_FILE_CMD_SENDER_SAY_HELLO:
327 case QQ_FILE_CMD_RECEIVER_SAY_HELLO:
328 case QQ_FILE_CMD_SENDER_SAY_HELLO_ACK:
329 case QQ_FILE_CMD_RECEIVER_SAY_HELLO_ACK:
330 bytes += create_packet_b(raw_data, &cursor, 0x00);
331 bytes += create_packet_b(raw_data, &cursor, hellobyte);
332 bytes_expected = 48;
333 break;
334 case QQ_FILE_CMD_PING:
335 case QQ_FILE_CMD_PONG:
336 case QQ_FILE_CMD_NOTIFY_IP_ACK:
337 bytes += qq_fill_conn_info(raw_data, &cursor, info);
338 bytes_expected = 61;
339 break;
340 default:
341 gaim_debug(GAIM_DEBUG_INFO, "QQ", "qq_send_file_ctl_packet: Unknown packet type[%d]\n",
342 packet_type);
343 bytes_expected = 0;
344 }
345
346 if (bytes == bytes_expected) {
347 gchar *hex_dump = hex_dump_to_str(raw_data, bytes);
348 gaim_debug(GAIM_DEBUG_INFO, "QQ", "sending packet[%s]: \n%s", qq_get_file_cmd_desc(packet_type), hex_dump);
349 g_free(hex_dump);
350 encrypted_len = bytes + 16;
351 encrypted_data = g_newa(guint8, encrypted_len);
352 qq_crypt(ENCRYPT, raw_data, bytes, info->file_session_key, encrypted_data, &encrypted_len);
353 /*debug: try to decrypt it */
354 /*
355 if (QQ_DEBUG) {
356 guint8 *buf;
357 int buflen;
358 hex_dump = hex_dump_to_str(encrypted_data, encrypted_len);
359 gaim_debug(GAIM_DEBUG_INFO, "QQ", "encrypted packet: \n%s", hex_dump);
360 g_free(hex_dump);
361 buf = g_newa(guint8, MAX_PACKET_SIZE);
362 buflen = encrypted_len;
363 if (qq_crypt(DECRYPT, encrypted_data, encrypted_len, info->file_session_key, buf, &buflen)) {
364 gaim_debug(GAIM_DEBUG_INFO, "QQ", "decrypt success\n");
365 if (buflen == bytes && memcmp(raw_data, buf, buflen) == 0)
366 gaim_debug(GAIM_DEBUG_INFO, "QQ", "checksum ok\n");
367 hex_dump = hex_dump_to_str(buf, buflen);
368 gaim_debug(GAIM_DEBUG_INFO, "QQ", "decrypted packet: \n%s", hex_dump);
369 g_free(hex_dump);
370 } else {
371 gaim_debug(GAIM_DEBUG_INFO, "QQ", "decrypt fail\n");
372 }
373 }
374 */
375
376 gaim_debug(GAIM_DEBUG_INFO, "QQ", "<== send %s packet\n", qq_get_file_cmd_desc(packet_type));
377 _qq_send_file(gc, encrypted_data, encrypted_len, QQ_FILE_CONTROL_PACKET_TAG, info->to_uid);
378 }
379 else
380 gaim_debug(GAIM_DEBUG_ERROR, "QQ", "qq_send_file_ctl_packet: Expected to get %d bytes, but get %d",
381 bytes_expected, bytes);
382 }
383
384 /* send a file to udp channel with QQ_FILE_DATA_PACKET_TAG */
385 static void _qq_send_file_data_packet(GaimConnection *gc, guint16 packet_type, guint8 sub_type,
386 guint32 fragment_index, guint16 seq, guint8 *data, gint len)
387 {
388 gint bytes;
389 guint8 *raw_data, *cursor, filename_md5[QQ_KEY_LENGTH], file_md5[QQ_KEY_LENGTH];
390 guint32 fragment_size = 1000;
391 gchar *filename;
392 gint filename_len, filesize;
393 qq_data *qd;
394 ft_info *info;
395
396 qd = (qq_data *) gc->proto_data;
397 info = (ft_info *) qd->xfer->data;
398
399 filename = (gchar *) gaim_xfer_get_filename(qd->xfer);
400 filesize = gaim_xfer_get_size(qd->xfer);
401
402 raw_data = g_newa(guint8, MAX_PACKET_SIZE);
403 cursor = raw_data;
404 bytes = 0;
405
406 bytes += create_packet_b(raw_data, &cursor, 0x00);
407 bytes += create_packet_w(raw_data, &cursor, packet_type);
408 switch (packet_type) {
409 case QQ_FILE_BASIC_INFO:
410 case QQ_FILE_DATA_INFO:
411 case QQ_FILE_EOF:
412 bytes += create_packet_w(raw_data, &cursor, 0x0000);
413 bytes += create_packet_b(raw_data, &cursor, 0x00);
414 break;
415 case QQ_FILE_CMD_FILE_OP:
416 switch(sub_type)
417 {
418 case QQ_FILE_BASIC_INFO:
419 filename_len = strlen(filename);
420 _fill_filename_md5(filename, filename_md5);
421 _fill_file_md5(gaim_xfer_get_local_filename(qd->xfer),
422 gaim_xfer_get_size(qd->xfer),
423 file_md5);
424
425 info->fragment_num = (filesize - 1) / QQ_FILE_FRAGMENT_MAXLEN + 1;
426 info->fragment_len = QQ_FILE_FRAGMENT_MAXLEN;
427
428 gaim_debug(GAIM_DEBUG_INFO, "QQ",
429 "start transfering data, %d fragments with %d length each\n",
430 info->fragment_num, info->fragment_len);
431 /* Unknown */
432 bytes += create_packet_w(raw_data, &cursor, 0x0000);
433 /* Sub-operation type */
434 bytes += create_packet_b(raw_data, &cursor, sub_type);
435 /* Length of file */
436 bytes += create_packet_dw(raw_data, &cursor, filesize);
437 /* Number of fragments */
438 bytes += create_packet_dw(raw_data, &cursor, info->fragment_num);
439 /* Length of a single fragment */
440 bytes += create_packet_dw(raw_data, &cursor, info->fragment_len);
441 bytes += create_packet_data(raw_data, &cursor, file_md5, 16);
442 bytes += create_packet_data(raw_data, &cursor, filename_md5, 16);
443 /* Length of filename */
444 bytes += create_packet_w(raw_data, &cursor, filename_len);
445 /* 8 unknown bytes */
446 bytes += create_packet_dw(raw_data, &cursor, 0x00000000);
447 bytes += create_packet_dw(raw_data, &cursor, 0x00000000);
448 /* filename */
449 bytes += create_packet_data(raw_data, &cursor, (guint8 *) filename,
450 filename_len);
451 break;
452 case QQ_FILE_DATA_INFO:
453 gaim_debug(GAIM_DEBUG_INFO, "QQ",
454 "sending %dth fragment with length %d, offset %d\n",
455 fragment_index, len, (fragment_index-1)*fragment_size);
456 /* bytes += create_packet_w(raw_data, &cursor, ++(qd->send_seq)); */
457 bytes += create_packet_w(raw_data, &cursor, info->send_seq);
458 bytes += create_packet_b(raw_data, &cursor, sub_type);
459 /* bytes += create_packet_dw(raw_data, &cursor, fragment_index); */
460 bytes += create_packet_dw(raw_data, &cursor, fragment_index - 1);
461 bytes += create_packet_dw(raw_data, &cursor, (fragment_index - 1) * fragment_size);
462 bytes += create_packet_w(raw_data, &cursor, len);
463 bytes += create_packet_data(raw_data, &cursor, data, len);
464 break;
465 case QQ_FILE_EOF:
466 gaim_debug(GAIM_DEBUG_INFO, "QQ", "end of sending data\n");
467 /* bytes += create_packet_w(raw_data, &cursor, info->fragment_num + 1); */
468 bytes += create_packet_w(raw_data, &cursor, info->fragment_num);
469 bytes += create_packet_b(raw_data, &cursor, sub_type);
470 /* gaim_xfer_set_completed(qd->xfer, TRUE); */
471 }
472 break;
473 case QQ_FILE_CMD_FILE_OP_ACK:
474 switch (sub_type)
475 {
476 case QQ_FILE_BASIC_INFO:
477 bytes += create_packet_w(raw_data, &cursor, 0x0000);
478 bytes += create_packet_b(raw_data, &cursor, sub_type);
479 bytes += create_packet_dw(raw_data, &cursor, 0x00000000);
480 break;
481 case QQ_FILE_DATA_INFO:
482 bytes += create_packet_w(raw_data, &cursor, seq);
483 bytes += create_packet_b(raw_data, &cursor, sub_type);
484 bytes += create_packet_dw(raw_data, &cursor, fragment_index);
485 break;
486 case QQ_FILE_EOF:
487 bytes += create_packet_w(raw_data, &cursor, filesize / QQ_FILE_FRAGMENT_MAXLEN + 2);
488 bytes += create_packet_b(raw_data, &cursor, sub_type);
489 break;
490 }
491 }
492 gaim_debug(GAIM_DEBUG_INFO, "QQ", "<== send %s packet\n", qq_get_file_cmd_desc(packet_type));
493 _qq_send_file(gc, raw_data, bytes, QQ_FILE_DATA_PACKET_TAG, info->to_uid);
494 }
495
496 /* A conversation starts like this:
497 * Sender ==> Receiver [QQ_FILE_CMD_PING]
498 * Sender <== Receiver [QQ_FILE_CMD_PONG]
499 * Sender ==> Receiver [QQ_FILE_CMD_SENDER_SAY_HELLO]
500 * Sender <== Receiver [QQ_FILE_CMD_SENDER_SAY_HELLO_ACK]
501 * Sender <== Receiver [QQ_FILE_CMD_RECEIVER_SAY_HELLO]
502 * Sender ==> Receiver [QQ_FILE_CMD_RECEIVER_SAY_HELLO_ACK]
503 * Sender ==> Receiver [QQ_FILE_CMD_FILE_OP, QQ_FILE_BASIC_INFO]
504 * Sender <== Receiver [QQ_FILE_CMD_FILE_OP_ACK, QQ_FILE_BASIC_INFO]
505 * Sender ==> Receiver [QQ_FILE_CMD_FILE_OP, QQ_FILE_DATA_INFO]
506 * Sender <== Receiver [QQ_FILE_CMD_FILE_OP_ACK, QQ_FILE_DATA_INFO]
507 * Sender ==> Receiver [QQ_FILE_CMD_FILE_OP, QQ_FILE_DATA_INFO]
508 * Sender <== Receiver [QQ_FILE_CMD_FILE_OP_ACK, QQ_FILE_DATA_INFO]
509 * ......
510 * Sender ==> Receiver [QQ_FILE_CMD_FILE_OP, QQ_FILE_EOF]
511 * Sender <== Receiver [QQ_FILE_CMD_FILE_OP_ACK, QQ_FILE_EOF]
512 */
513
514
515 static void _qq_process_recv_file_ctl_packet(GaimConnection *gc, guint8 *data, guint8 *cursor,
516 gint len, qq_file_header *fh)
517 {
518 guint8 *decrypted_data;
519 gint decrypted_len;
520 qq_data *qd = (qq_data *) gc->proto_data;
521 guint16 packet_type;
522 guint16 seq;
523 guint8 hellobyte;
524 ft_info *info = (ft_info *) qd->xfer->data;
525
526 decrypted_data = g_newa(guint8, len);
527 decrypted_len = len;
528
529 if (qq_crypt(DECRYPT, cursor, len - (cursor - data), qd->session_md5, decrypted_data, &decrypted_len)) {
530 gchar *hex_dump;
531 cursor = decrypted_data + 16; /* skip md5 section */
532 read_packet_w(decrypted_data, &cursor, decrypted_len, &packet_type);
533 read_packet_w(decrypted_data, &cursor, decrypted_len, &seq);
534 cursor += 4+1+1+19+1;
535 gaim_debug(GAIM_DEBUG_INFO, "QQ", "==> [%d] receive %s packet\n", seq, qq_get_file_cmd_desc(packet_type));
536 hex_dump = hex_dump_to_str(decrypted_data, decrypted_len);
537 gaim_debug(GAIM_DEBUG_INFO, "QQ", "decrypted control packet received: \n%s", hex_dump);
538 g_free(hex_dump);
539 switch (packet_type) {
540 case QQ_FILE_CMD_NOTIFY_IP_ACK:
541 cursor = decrypted_data;
542 qq_get_conn_info(decrypted_data, &cursor, decrypted_len, info);
543 /* qq_send_file_ctl_packet(gc, QQ_FILE_CMD_PING, fh->sender_uid, 0); */
544 qq_send_file_ctl_packet(gc, QQ_FILE_CMD_SENDER_SAY_HELLO, fh->sender_uid, 0);
545 break;
546 case QQ_FILE_CMD_SENDER_SAY_HELLO:
547 /* I'm receiver, if we receive SAY_HELLO from sender, we send back the ACK */
548 cursor += 47;
549 read_packet_b(decrypted_data, &cursor,
550 decrypted_len, &hellobyte);
551
552 qq_send_file_ctl_packet(gc, QQ_FILE_CMD_SENDER_SAY_HELLO_ACK, fh->sender_uid, hellobyte);
553 qq_send_file_ctl_packet(gc, QQ_FILE_CMD_RECEIVER_SAY_HELLO, fh->sender_uid, 0);
554 break;
555 case QQ_FILE_CMD_SENDER_SAY_HELLO_ACK:
556 /* I'm sender, do nothing */
557 break;
558 case QQ_FILE_CMD_RECEIVER_SAY_HELLO:
559 /* I'm sender, ack the hello packet and send the first data */
560 cursor += 47;
561 read_packet_b(decrypted_data, &cursor,
562 decrypted_len, &hellobyte);
563 qq_send_file_ctl_packet(gc, QQ_FILE_CMD_RECEIVER_SAY_HELLO_ACK, fh->sender_uid, hellobyte);
564 _qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP, QQ_FILE_BASIC_INFO, 0, 0, NULL, 0);
565 break;
566 case QQ_FILE_CMD_RECEIVER_SAY_HELLO_ACK:
567 /* I'm receiver, do nothing */
568 break;
569 case QQ_FILE_CMD_PING:
570 /* I'm receiver, ack the PING */
571 qq_send_file_ctl_packet(gc, QQ_FILE_CMD_PONG, fh->sender_uid, 0);
572 break;
573 case QQ_FILE_CMD_PONG:
574 qq_send_file_ctl_packet(gc, QQ_FILE_CMD_SENDER_SAY_HELLO, fh->sender_uid, 0);
575 break;
576 default:
577 gaim_debug(GAIM_DEBUG_INFO, "QQ", "unprocess file command %d\n", packet_type);
578 }
579 }
580 }
581
582 static void _qq_recv_file_progess(GaimConnection *gc, guint8 *buffer, guint16 len, guint32 index, guint32 offset)
583 {
584 qq_data *qd = (qq_data *) gc->proto_data;
585 GaimXfer *xfer = qd->xfer;
586 ft_info *info = (ft_info *) xfer->data;
587 guint32 mask;
588
589 gaim_debug(GAIM_DEBUG_INFO, "QQ",
590 "receiving %dth fragment with length %d, slide window status %o, max_fragment_index %d\n",
591 index, len, info->window, info->max_fragment_index);
592 if (info->window == 0 && info->max_fragment_index == 0) {
593 if (_qq_xfer_open_file(gaim_xfer_get_local_filename(xfer), "wb", xfer) == -1) {
594 gaim_xfer_cancel_local(xfer);
595 return;
596 }
597 gaim_debug(GAIM_DEBUG_INFO, "QQ", "object file opened for writing\n");
598 }
599 mask = 0x1 << (index % sizeof(info->window));
600 if (index < info->max_fragment_index || (info->window & mask)) {
601 gaim_debug(GAIM_DEBUG_INFO, "QQ", "duplicate %dth fragment, drop it!\n", index+1);
602 return;
603 }
604
605 info->window |= mask;
606
607 _qq_xfer_write_file(buffer, index, len, xfer);
608
609 xfer->bytes_sent += len;
610 xfer->bytes_remaining -= len;
611 gaim_xfer_update_progress(xfer);
612
613 mask = 0x1 << (info->max_fragment_index % sizeof(info->window));
614 while (info->window & mask)
615 {
616 info->window &= ~mask;
617 info->max_fragment_index ++;
618 if (mask & 0x8000) mask = 0x0001;
619 else mask = mask << 1;
620 }
621 gaim_debug(GAIM_DEBUG_INFO, "QQ", "procceed %dth fragment, slide window status %o, max_fragment_index %d\n",
622 index, info->window, info->max_fragment_index);
623 }
624
625 static void _qq_send_file_progess(GaimConnection *gc)
626 {
627 qq_data *qd = (qq_data *) gc->proto_data;
628 GaimXfer *xfer = qd->xfer;
629 ft_info *info = (ft_info *) xfer->data;
630 guint32 mask;
631 guint8 *buffer;
632 guint i;
633 gint readbytes;
634
635 if (gaim_xfer_get_bytes_remaining(xfer) <= 0) return;
636 if (info->window == 0 && info->max_fragment_index == 0)
637 {
638 if (_qq_xfer_open_file(gaim_xfer_get_local_filename(xfer), "rb", xfer) == -1) {
639 gaim_xfer_cancel_local(xfer);
640 return;
641 }
642 }
643 buffer = g_newa(guint8, info->fragment_len);
644 mask = 0x1 << (info->max_fragment_index % sizeof(info->window));
645 for (i = 0; i < sizeof(info->window); i++) {
646 if ((info->window & mask) == 0) {
647 readbytes = _qq_xfer_read_file(buffer, info->max_fragment_index + i, info->fragment_len, xfer);
648 if (readbytes > 0)
649 _qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP, QQ_FILE_DATA_INFO,
650 info->max_fragment_index + i + 1, 0, buffer, readbytes);
651 }
652 if (mask & 0x8000) mask = 0x0001;
653 else mask = mask << 1;
654 }
655 }
656
657 static void _qq_update_send_progess(GaimConnection *gc, guint32 fragment_index)
658 {
659 guint32 mask;
660 guint8 *buffer;
661 gint readbytes;
662 qq_data *qd = (qq_data *) gc->proto_data;
663 GaimXfer *xfer = qd->xfer;
664 ft_info *info = (ft_info *) xfer->data;
665
666 gaim_debug(GAIM_DEBUG_INFO, "QQ",
667 "receiving %dth fragment ack, slide window status %o, max_fragment_index %d\n",
668 fragment_index, info->window, info->max_fragment_index);
669 if (fragment_index < info->max_fragment_index ||
670 fragment_index >= info->max_fragment_index + sizeof(info->window)) {
671 gaim_debug(GAIM_DEBUG_INFO, "QQ", "duplicate %dth fragment, drop it!\n", fragment_index+1);
672 return;
673 }
674 mask = 0x1 << (fragment_index % sizeof(info->window));
675 if ((info->window & mask) == 0)
676 {
677 info->window |= mask;
678 if (fragment_index + 1 != info->fragment_num) {
679 xfer->bytes_sent += info->fragment_len;
680 } else {
681 xfer->bytes_sent += gaim_xfer_get_size(xfer) % info->fragment_len;
682 }
683 xfer->bytes_remaining = gaim_xfer_get_size(xfer) - gaim_xfer_get_bytes_sent(xfer);
684 gaim_xfer_update_progress(xfer);
685 if (gaim_xfer_get_bytes_remaining(xfer) <= 0) {
686 /* We have finished sending the file */
687 gaim_xfer_set_completed(xfer, TRUE);
688 return;
689 }
690 mask = 0x1 << (info->max_fragment_index % sizeof(info->window));
691 while (info->window & mask)
692 {
693 /* move the slide window */
694 info->window &= ~mask;
695
696 buffer = g_newa(guint8, info->fragment_len);
697 readbytes = _qq_xfer_read_file(buffer, info->max_fragment_index + sizeof(info->window),
698 info->fragment_len, xfer);
699 if (readbytes > 0)
700 _qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP, QQ_FILE_DATA_INFO,
701 info->max_fragment_index + sizeof(info->window) + 1, 0, buffer, readbytes);
702
703 info->max_fragment_index ++;
704 if (mask & 0x8000) mask = 0x0001;
705 else mask = mask << 1;
706 }
707 }
708 gaim_debug(GAIM_DEBUG_INFO, "QQ",
709 "procceed %dth fragment ack, slide window status %o, max_fragment_index %d\n",
710 fragment_index, info->window, info->max_fragment_index);
711 }
712
713 static void _qq_process_recv_file_data(GaimConnection *gc, guint8 *data, guint8 *cursor,
714 gint len, guint32 to_uid)
715 {
716 guint16 packet_type;
717 guint16 packet_seq;
718 guint8 sub_type;
719 guint32 fragment_index;
720 guint16 fragment_len;
721 guint32 fragment_offset;
722 qq_data *qd = (qq_data *) gc->proto_data;
723 ft_info *info = (ft_info *) qd->xfer->data;
724
725 cursor += 1; /* skip an unknown byte */
726 read_packet_w(data, &cursor, len, &packet_type);
727 switch(packet_type)
728 {
729 case QQ_FILE_CMD_FILE_OP:
730 read_packet_w(data, &cursor, len, &packet_seq);
731 read_packet_b(data, &cursor, len, &sub_type);
732 switch (sub_type)
733 {
734 case QQ_FILE_BASIC_INFO:
735 cursor += 4; /* file length, we have already known it from xfer */
736 read_packet_dw(data, &cursor, len, &info->fragment_num);
737 read_packet_dw(data, &cursor, len, &info->fragment_len);
738
739 /* FIXME: We must check the md5 here, if md5 doesn't match
740 * we will ignore the packet or send sth as error number
741 */
742
743 info->max_fragment_index = 0;
744 info->window = 0;
745 gaim_debug(GAIM_DEBUG_INFO, "QQ",
746 "start receiving data, %d fragments with %d length each\n",
747 info->fragment_num, info->fragment_len);
748 _qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP_ACK, sub_type,
749 0, 0, NULL, 0);
750 break;
751 case QQ_FILE_DATA_INFO:
752 read_packet_dw(data, &cursor, len, &fragment_index);
753 read_packet_dw(data, &cursor, len, &fragment_offset);
754 read_packet_w(data, &cursor, len, &fragment_len);
755 gaim_debug(GAIM_DEBUG_INFO, "QQ",
756 "received %dth fragment with length %d, offset %d\n",
757 fragment_index, fragment_len, fragment_offset);
758
759 _qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP_ACK, sub_type,
760 fragment_index, packet_seq, NULL, 0);
761 _qq_recv_file_progess(gc, cursor, fragment_len, fragment_index, fragment_offset);
762 break;
763 case QQ_FILE_EOF:
764 gaim_debug(GAIM_DEBUG_INFO, "QQ", "end of receiving\n");
765 _qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP_ACK, sub_type,
766 0, 0, NULL, 0);
767 break;
768 }
769 break;
770 case QQ_FILE_CMD_FILE_OP_ACK:
771 read_packet_w(data, &cursor, len, &packet_seq);
772 read_packet_b(data, &cursor, len, &sub_type);
773 switch (sub_type)
774 {
775 case QQ_FILE_BASIC_INFO:
776 info->max_fragment_index = 0;
777 info->window = 0;
778 /* It is ready to send file data */
779 _qq_send_file_progess(gc);
780 break;
781 case QQ_FILE_DATA_INFO:
782 read_packet_dw(data, &cursor, len, &fragment_index);
783 _qq_update_send_progess(gc, fragment_index);
784 if (gaim_xfer_is_completed(qd->xfer))
785 _qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP, QQ_FILE_EOF, 0, 0, NULL, 0);
786 /* else
787 _qq_send_file_progess(gc); */
788 break;
789 case QQ_FILE_EOF:
790 /* FIXME: OK, we can end the connection successfully */
791
792 _qq_send_file_data_packet(gc, QQ_FILE_EOF, 0, 0, 0, NULL, 0);
793 gaim_xfer_set_completed(qd->xfer, TRUE);
794 break;
795 }
796 break;
797 case QQ_FILE_EOF:
798 _qq_send_file_data_packet(gc, QQ_FILE_EOF, 0, 0, 0, NULL, 0);
799 gaim_xfer_set_completed(qd->xfer, TRUE);
800 gaim_xfer_end(qd->xfer);
801 break;
802 case QQ_FILE_BASIC_INFO:
803 gaim_debug(GAIM_DEBUG_INFO, "QQ", "here\n");
804 _qq_send_file_data_packet(gc, QQ_FILE_DATA_INFO, 0, 0, 0, NULL, 0);
805 break;
806 default:
807 gaim_debug(GAIM_DEBUG_INFO, "QQ", "_qq_process_recv_file_data: unknown packet type [%d]\n",
808 packet_type);
809 break;
810 }
811 }
812
813 void qq_process_recv_file(GaimConnection *gc, guint8 *data, gint len)
814 {
815 guint8 *cursor;
816 qq_file_header fh;
817 qq_data *qd;
818
819 qd = (qq_data *) gc->proto_data;
820
821 cursor = data;
822 _qq_get_file_header(data, &cursor, len, &fh);
823
824 switch (fh.tag) {
825 case QQ_FILE_CONTROL_PACKET_TAG:
826 _qq_process_recv_file_ctl_packet(gc, data, cursor, len, &fh);
827 break;
828 case QQ_FILE_DATA_PACKET_TAG:
829 _qq_process_recv_file_data(gc, data, cursor, len, fh.sender_uid);
830 break;
831 default:
832 gaim_debug(GAIM_DEBUG_INFO, "QQ", "unknown packet tag");
833 }
834 }