00001
00025 #ifdef _WIN32
00026 #define random rand
00027 #endif
00028
00029 #include "debug.h"
00030 #include "ft.h"
00031 #include "md5.h"
00032
00033 #include "file_trans.h"
00034 #include "send_file.h"
00035 #include "packet_parse.h"
00036 #include "qq.h"
00037 #include "header_info.h"
00038 #include "im.h"
00039 #include "crypt.h"
00040 #include "proxy.h"
00041
00042 extern gchar*
00043 hex_dump_to_str (const guint8 * buffer, gint bytes);
00044
00045 struct _qq_file_header {
00046 guint8 tag;
00047 guint16 client_ver;
00048 guint8 file_key;
00049 guint32 sender_uid;
00050 guint32 receiver_uid;
00051 };
00052
00053 typedef struct _qq_file_header qq_file_header;
00054
00055 guint32 _get_file_key(guint8 seed)
00056 {
00057 guint32 key;
00058 key = seed | (seed << 8) | (seed << 16) | (seed << 24);
00059 return key;
00060 }
00061
00062 guint32 _gen_file_key()
00063 {
00064 guint8 seed;
00065
00066 seed = random();
00067 return _get_file_key(seed);
00068 }
00069
00070 guint32 _decrypt_qq_uid(guint32 uid, guint32 key)
00071 {
00072 return ~(uid ^ key);
00073 }
00074
00075 guint32 _encrypt_qq_uid(guint32 uid, guint32 key)
00076 {
00077 return (~uid) ^ key;
00078 }
00079
00080 void _fill_filename_md5(const gchar *filename, gchar *md5)
00081 {
00082 md5_state_t ctx;
00083
00084 g_return_if_fail(filename != NULL && md5 != NULL);
00085 md5_init(&ctx);
00086 md5_append(&ctx, filename, strlen(filename));
00087 md5_finish(&ctx, md5);
00088 }
00089
00090 void _fill_file_md5(const gchar *filename, gint filelen, gchar *md5)
00091 {
00092 FILE *fp;
00093 gchar *buffer;
00094 md5_state_t ctx;
00095 const gint QQ_MAX_FILE_MD5_LENGTH = 10002432;
00096
00097 g_return_if_fail(filename != NULL && md5 != NULL);
00098 if (filelen > QQ_MAX_FILE_MD5_LENGTH)
00099 filelen = QQ_MAX_FILE_MD5_LENGTH;
00100
00101 fp = fopen(filename, "rb");
00102 g_return_if_fail(fp != NULL);
00103
00104 buffer = g_newa(gchar, filelen);
00105 g_return_if_fail(buffer != NULL);
00106 fread(buffer, filelen, 1, fp);
00107 md5_init(&ctx);
00108 md5_append(&ctx, buffer, filelen);
00109 md5_finish(&ctx, md5);
00110 fclose(fp);
00111 }
00112
00113 void _qq_get_file_header(guint8 *buf, guint8 **cursor, gint buflen, qq_file_header *fh)
00114 {
00115 read_packet_b(buf, cursor, buflen, &(fh->tag));
00116 read_packet_w(buf, cursor, buflen, &(fh->client_ver));
00117 read_packet_b(buf, cursor, buflen, &fh->file_key);
00118 read_packet_dw(buf, cursor, buflen, &(fh->sender_uid));
00119 read_packet_dw(buf, cursor, buflen, &(fh->receiver_uid));
00120
00121 fh->sender_uid = _decrypt_qq_uid(fh->sender_uid, _get_file_key(fh->file_key));
00122 fh->receiver_uid = _decrypt_qq_uid(fh->receiver_uid, _get_file_key(fh->file_key));
00123 }
00124
00125 const gchar *qq_get_file_cmd_desc(gint type)
00126 {
00127 switch (type) {
00128 case QQ_FILE_CMD_SENDER_SAY_HELLO:
00129 return "QQ_FILE_CMD_SENDER_SAY_HELLO";
00130 case QQ_FILE_CMD_SENDER_SAY_HELLO_ACK:
00131 return "QQ_FILE_CMD_SENDER_SAY_HELLO_ACK";
00132 case QQ_FILE_CMD_RECEIVER_SAY_HELLO:
00133 return "QQ_FILE_CMD_RECEIVER_SAY_HELLO";
00134 case QQ_FILE_CMD_RECEIVER_SAY_HELLO_ACK:
00135 return "QQ_FILE_CMD_RECEIVER_SAY_HELLO_ACK";
00136 case QQ_FILE_CMD_NOTIFY_IP_ACK:
00137 return "QQ_FILE_CMD_NOTIFY_IP_ACK";
00138 case QQ_FILE_CMD_PING:
00139 return "QQ_FILE_CMD_PING";
00140 case QQ_FILE_CMD_PONG:
00141 return "QQ_FILE_CMD_PONG";
00142 case QQ_FILE_CMD_INITATIVE_CONNECT:
00143 return "QQ_FILE_CMD_INITATIVE_CONNECT";
00144 case QQ_FILE_CMD_FILE_OP:
00145 return "QQ_FILE_CMD_FILE_OP";
00146 case QQ_FILE_CMD_FILE_OP_ACK:
00147 return "QQ_FILE_CMD_FILE_OP_ACK";
00148 case QQ_FILE_BASIC_INFO:
00149 return "QQ_FILE_BASIC_INFO";
00150 case QQ_FILE_DATA_INFO:
00151 return "QQ_FILE_DATA_INFO";
00152 case QQ_FILE_EOF:
00153 return "QQ_FILE_EOF";
00154 default:
00155 return "UNKNOWN_TYPE";
00156 }
00157 }
00158
00159
00160
00161
00162 #ifdef USE_MMAP
00163 #include <sys/mman.h>
00164
00165 int _qq_xfer_open_file(const gchar *filename, const gchar *method, GaimXfer *xfer)
00166 {
00167 ft_info *info = xfer->data;
00168 int fd;
00169 if (method[0] == 'r') {
00170 fd = open(gaim_xfer_get_local_filename(xfer), O_RDONLY);
00171 info->buffer = mmap(0, gaim_xfer_get_size(xfer), PROT_READ, MAP_PRIVATE, fd, 0);
00172 }
00173 else
00174 {
00175 fd = open(gaim_xfer_get_local_filename(xfer), O_RDWR|O_CREAT, 0644);
00176 info->buffer = mmap(0, gaim_xfer_get_size(xfer), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FILE, fd, 0);
00177 }
00178
00179 if (info->buffer == NULL) {
00180 return - 1;
00181 }
00182 return 0;
00183 }
00184
00185 gint
00186 _qq_xfer_read_file(guint8 *buffer, guint index, guint len, GaimXfer *xfer)
00187 {
00188 ft_info *info = xfer->data;
00189 gint readbytes;
00190
00191 buffer = info->buffer + len * index;
00192 readbytes = gaim_xfer_get_size(xfer) - (buffer - info->buffer);
00193 if (readbytes > info->fragment_len) readbytes = info->fragment_len;
00194 return readbytes;
00195 }
00196
00197 gint
00198 _qq_xfer_write_file(guint8 *buffer, guint index, guint len, GaimXfer *xfer)
00199 {
00200 ft_info *info = xfer->data;
00201
00202 memcpy(info->buffer + index * len, buffer, len);
00203 return 0;
00204 }
00205
00206 void qq_xfer_close_file(GaimXfer *xfer)
00207 {
00208 ft_info *info = xfer->data;
00209
00210 if (info->buffer) munmap(info->buffer, gaim_xfer_get_size(xfer));
00211 }
00212 #else
00213 int _qq_xfer_open_file(const gchar *filename, const gchar *method, GaimXfer *xfer)
00214 {
00215 ft_info *info = xfer->data;
00216 info->dest_fp = fopen(gaim_xfer_get_local_filename(xfer), method);
00217 if (info->dest_fp == NULL) {
00218 return -1;
00219 }
00220 return 0;
00221 }
00222
00223 gint
00224 _qq_xfer_read_file(guint8 *buffer, guint index, guint len, GaimXfer *xfer)
00225 {
00226 ft_info *info = xfer->data;
00227
00228 fseek(info->dest_fp, index * len, SEEK_SET);
00229 return fread(buffer, 1, len, info->dest_fp);
00230 }
00231
00232 gint
00233 _qq_xfer_write_file(guint8 *buffer, guint index, guint len, GaimXfer *xfer)
00234 {
00235 ft_info *info = xfer->data;
00236 fseek(info->dest_fp, index * len, SEEK_SET);
00237 return fwrite(buffer, 1, len, info->dest_fp);
00238 }
00239
00240 void qq_xfer_close_file(GaimXfer *xfer)
00241 {
00242 ft_info *info = xfer->data;
00243
00244 if (info->dest_fp) fclose(info->dest_fp);
00245 }
00246 #endif
00247
00248 gint _qq_send_file(GaimConnection *gc, guint8 *data, gint len, guint16 packet_type, guint32 to_uid)
00249 {
00250 gint bytes;
00251 guint8 *cursor, *buf;
00252 guint32 file_key;
00253 qq_data *qd;
00254 ft_info *info;
00255
00256 g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, -1);
00257 qd = (qq_data *) gc->proto_data;
00258 g_return_val_if_fail(qd != NULL && qd->session_key != NULL, -1);
00259 info = (ft_info *) qd->xfer->data;
00260 bytes = 0;
00261
00262 buf = g_newa(guint8, MAX_PACKET_SIZE);
00263 cursor = buf;
00264 file_key = _gen_file_key();
00265
00266 bytes += create_packet_b(buf, &cursor, packet_type);
00267 bytes += create_packet_w(buf, &cursor, QQ_CLIENT);
00268 bytes += create_packet_b(buf, &cursor, file_key & 0xff);
00269 bytes += create_packet_dw(buf, &cursor, _encrypt_qq_uid(qd->uid, file_key));
00270 bytes += create_packet_dw(buf, &cursor, _encrypt_qq_uid(to_uid, file_key));
00271 bytes += create_packet_data(buf, &cursor, data, len);
00272
00273 ssize_t _qq_xfer_write(const char *buf, size_t len, GaimXfer *xfer);
00274 if (bytes == len + 12) {
00275
00276 _qq_xfer_write(buf, bytes, qd->xfer);
00277 } else
00278 gaim_debug(GAIM_DEBUG_INFO, "QQ", "send_file: want %d but got %d\n", len + 12, bytes);
00279 return bytes;
00280 }
00281
00282
00283 extern gchar *_gen_session_md5(gint uid, gchar *session_key);
00284
00285
00286
00287 void qq_send_file_ctl_packet(GaimConnection *gc, guint16 packet_type, guint32 to_uid, guint8 hellobyte)
00288 {
00289 qq_data *qd;
00290 gint bytes, bytes_expected, encrypted_len;
00291 guint8 *raw_data, *cursor, *encrypted_data;
00292 gchar *md5;
00293 time_t now;
00294 ft_info *info;
00295
00296 g_return_if_fail(gc != NULL && gc->proto_data != NULL);
00297 qd = (qq_data *) gc->proto_data;
00298 info = (ft_info *) qd->xfer->data;
00299
00300 raw_data = g_new0 (guint8, 61);
00301 cursor = raw_data;
00302
00303 bytes = 0;
00304 now = time(NULL);
00305 md5 = _gen_session_md5(qd->uid, qd->session_key);
00306
00307 bytes += create_packet_data(raw_data, &cursor, md5, 16);
00308 bytes += create_packet_w(raw_data, &cursor, packet_type);
00309 switch (packet_type) {
00310 case QQ_FILE_CMD_SENDER_SAY_HELLO:
00311 case QQ_FILE_CMD_SENDER_SAY_HELLO_ACK:
00312 case QQ_FILE_CMD_RECEIVER_SAY_HELLO_ACK:
00313 case QQ_FILE_CMD_NOTIFY_IP_ACK:
00314 case QQ_FILE_CMD_RECEIVER_SAY_HELLO:
00315 bytes += create_packet_w(raw_data, &cursor, info->send_seq);
00316 break;
00317 default:
00318 bytes += create_packet_w(raw_data, &cursor, ++qd->send_seq);
00319 }
00320 bytes += create_packet_dw(raw_data, &cursor, (guint32) now);
00321 bytes += create_packet_b(raw_data, &cursor, 0x00);
00322 bytes += create_packet_b(raw_data, &cursor, qd->my_icon);
00323 bytes += create_packet_dw(raw_data, &cursor, 0x00000000);
00324 bytes += create_packet_dw(raw_data, &cursor, 0x00000000);
00325 bytes += create_packet_dw(raw_data, &cursor, 0x00000000);
00326 bytes += create_packet_dw(raw_data, &cursor, 0x00000000);
00327 bytes += create_packet_w(raw_data, &cursor, 0x0000);
00328 bytes += create_packet_b(raw_data, &cursor, 0x00);
00329 bytes += create_packet_b(raw_data, &cursor, 0x65);
00330 switch (packet_type)
00331 {
00332 case QQ_FILE_CMD_SENDER_SAY_HELLO:
00333 case QQ_FILE_CMD_RECEIVER_SAY_HELLO:
00334 case QQ_FILE_CMD_SENDER_SAY_HELLO_ACK:
00335 case QQ_FILE_CMD_RECEIVER_SAY_HELLO_ACK:
00336 bytes += create_packet_b(raw_data, &cursor, 0x00);
00337 bytes += create_packet_b(raw_data, &cursor, hellobyte);
00338 bytes_expected = 48;
00339 break;
00340 case QQ_FILE_CMD_PING:
00341 case QQ_FILE_CMD_PONG:
00342 case QQ_FILE_CMD_NOTIFY_IP_ACK:
00343 bytes += qq_fill_conn_info(raw_data, &cursor, info);
00344 bytes_expected = 61;
00345 break;
00346 default:
00347 gaim_debug(GAIM_DEBUG_INFO, "QQ", "qq_send_file_ctl_packet: Unknown packet type[%d]\n",
00348 packet_type);
00349 bytes_expected = 0;
00350 }
00351
00352 if (bytes == bytes_expected) {
00353 gaim_debug(GAIM_DEBUG_INFO, "QQ", "sending packet[%s]: \n%s", qq_get_file_cmd_desc(packet_type),
00354 hex_dump_to_str(raw_data, bytes));
00355 encrypted_len = bytes + 16;
00356 encrypted_data = g_newa(guint8, encrypted_len);
00357 qq_crypt(ENCRYPT, raw_data, bytes, info->file_session_key, encrypted_data, &encrypted_len);
00358
00359
00360
00361
00362
00363
00364
00365
00366
00367
00368
00369
00370
00371
00372
00373
00374
00375
00376
00377
00378
00379 gaim_debug(GAIM_DEBUG_INFO, "QQ", "<== send %s packet\n", qq_get_file_cmd_desc(packet_type));
00380 _qq_send_file(gc, encrypted_data, encrypted_len, QQ_FILE_CONTROL_PACKET_TAG, info->to_uid);
00381 }
00382 else
00383 gaim_debug(GAIM_DEBUG_ERROR, "QQ", "qq_send_file_ctl_packet: Expected to get %d bytes, but get %d",
00384 bytes_expected, bytes);
00385
00386 g_free(md5);
00387 }
00388
00389
00390
00391 void _qq_send_file_data_packet(GaimConnection *gc, guint16 packet_type, guint8 sub_type, guint32 fragment_index,
00392 guint16 seq, guint8 *data, gint len)
00393 {
00394 gint bytes;
00395 guint8 *raw_data, *cursor;
00396 guint32 fragment_size = 1000;
00397 gchar file_md5[16], filename_md5[16], *filename;
00398 gint filename_len, filesize;
00399 qq_data *qd;
00400
00401 g_return_if_fail(gc != NULL && gc->proto_data != NULL);
00402 qd = (qq_data *) gc->proto_data;
00403 ft_info *info = (ft_info *) qd->xfer->data;
00404
00405 filename = (gchar *) gaim_xfer_get_filename(qd->xfer);
00406 filesize = gaim_xfer_get_size(qd->xfer);
00407
00408 raw_data = g_newa(guint8, MAX_PACKET_SIZE);
00409 cursor = raw_data;
00410 bytes = 0;
00411
00412 bytes += create_packet_b(raw_data, &cursor, 0x00);
00413 bytes += create_packet_w(raw_data, &cursor, packet_type);
00414 switch (packet_type) {
00415 case QQ_FILE_BASIC_INFO:
00416 case QQ_FILE_DATA_INFO:
00417 case QQ_FILE_EOF:
00418 bytes += create_packet_w(raw_data, &cursor, 0x0000);
00419 bytes += create_packet_b(raw_data, &cursor, 0x00);
00420 break;
00421 case QQ_FILE_CMD_FILE_OP:
00422 switch(sub_type)
00423 {
00424 case QQ_FILE_BASIC_INFO:
00425 filename_len = strlen(filename);
00426 _fill_filename_md5(filename, filename_md5);
00427 _fill_file_md5(gaim_xfer_get_local_filename(qd->xfer),
00428 gaim_xfer_get_size(qd->xfer),
00429 file_md5);
00430
00431 info->fragment_num = (filesize - 1) / QQ_FILE_FRAGMENT_MAXLEN + 1;
00432 info->fragment_len = QQ_FILE_FRAGMENT_MAXLEN;
00433
00434 gaim_debug(GAIM_DEBUG_INFO, "QQ", "start transfering data, %d fragments with %d length each\n",
00435 info->fragment_num, info->fragment_len);
00436
00437 bytes += create_packet_w(raw_data, &cursor, 0x0000);
00438
00439 bytes += create_packet_b(raw_data, &cursor, sub_type);
00440
00441 bytes += create_packet_dw(raw_data, &cursor, filesize);
00442
00443 bytes += create_packet_dw(raw_data, &cursor, info->fragment_num);
00444
00445 bytes += create_packet_dw(raw_data, &cursor, info->fragment_len);
00446 bytes += create_packet_data(raw_data, &cursor, file_md5, 16);
00447 bytes += create_packet_data(raw_data, &cursor, filename_md5, 16);
00448
00449 bytes += create_packet_w(raw_data, &cursor, filename_len);
00450
00451 bytes += create_packet_dw(raw_data, &cursor, 0x00000000);
00452 bytes += create_packet_dw(raw_data, &cursor, 0x00000000);
00453
00454 bytes += create_packet_data(raw_data, &cursor, (guint8 *) filename,
00455 filename_len);
00456 break;
00457 case QQ_FILE_DATA_INFO:
00458 gaim_debug(GAIM_DEBUG_INFO, "QQ", "sending %dth fragment with length %d, offset %d\n",
00459 fragment_index, len, (fragment_index-1)*fragment_size);
00460
00461 bytes += create_packet_w(raw_data, &cursor, info->send_seq);
00462 bytes += create_packet_b(raw_data, &cursor, sub_type);
00463
00464 bytes += create_packet_dw(raw_data, &cursor, fragment_index - 1);
00465 bytes += create_packet_dw(raw_data, &cursor, (fragment_index - 1) * fragment_size);
00466 bytes += create_packet_w(raw_data, &cursor, len);
00467 bytes += create_packet_data(raw_data, &cursor, data, len);
00468 break;
00469 case QQ_FILE_EOF:
00470 gaim_debug(GAIM_DEBUG_INFO, "QQ", "end of sending data\n");
00471
00472 bytes += create_packet_w(raw_data, &cursor, info->fragment_num);
00473 bytes += create_packet_b(raw_data, &cursor, sub_type);
00474
00475 }
00476 break;
00477 case QQ_FILE_CMD_FILE_OP_ACK:
00478 switch (sub_type)
00479 {
00480 case QQ_FILE_BASIC_INFO:
00481 bytes += create_packet_w(raw_data, &cursor, 0x0000);
00482 bytes += create_packet_b(raw_data, &cursor, sub_type);
00483 bytes += create_packet_dw(raw_data, &cursor, 0x00000000);
00484 break;
00485 case QQ_FILE_DATA_INFO:
00486 bytes += create_packet_w(raw_data, &cursor, seq);
00487 bytes += create_packet_b(raw_data, &cursor, sub_type);
00488 bytes += create_packet_dw(raw_data, &cursor, fragment_index);
00489 break;
00490 case QQ_FILE_EOF:
00491 bytes += create_packet_w(raw_data, &cursor, filesize / QQ_FILE_FRAGMENT_MAXLEN + 2);
00492 bytes += create_packet_b(raw_data, &cursor, sub_type);
00493 break;
00494 }
00495 }
00496 gaim_debug(GAIM_DEBUG_INFO, "QQ", "<== send %s packet\n", qq_get_file_cmd_desc(packet_type));
00497 _qq_send_file(gc, raw_data, bytes, QQ_FILE_DATA_PACKET_TAG, info->to_uid);
00498 }
00499
00500
00501
00502
00503
00504
00505
00506
00507
00508
00509
00510
00511
00512
00513
00514
00515
00516
00517
00518
00519 void
00520 _qq_process_recv_file_ctl_packet(GaimConnection *gc, guint8 *data, guint8 *cursor,
00521 gint len, qq_file_header *fh)
00522 {
00523 guint8 *decrypted_data;
00524 gint decrypted_len;
00525 qq_data *qd = (qq_data *) gc->proto_data;
00526 guint16 packet_type;
00527 guint16 seq;
00528 guint8 hellobyte;
00529 gchar *md5;
00530 ft_info *info = (ft_info *) qd->xfer->data;
00531
00532 decrypted_data = g_newa(guint8, len);
00533 decrypted_len = len;
00534
00535 md5 = _gen_session_md5(qd->uid, qd->session_key);
00536 if (qq_crypt(DECRYPT, cursor, len - (cursor - data), md5, decrypted_data, &decrypted_len)) {
00537 cursor = decrypted_data + 16;
00538 read_packet_w(decrypted_data, &cursor, decrypted_len, &packet_type);
00539 read_packet_w(decrypted_data, &cursor, decrypted_len, &seq);
00540 cursor += 4+1+1+19+1;
00541 gaim_debug(GAIM_DEBUG_INFO, "QQ", "==> [%d] receive %s packet\n", seq, qq_get_file_cmd_desc(packet_type));
00542 gaim_debug(GAIM_DEBUG_INFO, "QQ", "decrypted control packet received: \n%s",
00543 hex_dump_to_str(decrypted_data, decrypted_len));
00544 switch (packet_type) {
00545 case QQ_FILE_CMD_NOTIFY_IP_ACK:
00546 cursor = decrypted_data;
00547 qq_get_conn_info(decrypted_data, &cursor, decrypted_len, info);
00548
00549 qq_send_file_ctl_packet(gc, QQ_FILE_CMD_SENDER_SAY_HELLO, fh->sender_uid, 0);
00550 break;
00551 case QQ_FILE_CMD_SENDER_SAY_HELLO:
00552
00553 cursor += 47;
00554 read_packet_b(decrypted_data, &cursor,
00555 decrypted_len, &hellobyte);
00556
00557 qq_send_file_ctl_packet(gc, QQ_FILE_CMD_SENDER_SAY_HELLO_ACK, fh->sender_uid, hellobyte);
00558 qq_send_file_ctl_packet(gc, QQ_FILE_CMD_RECEIVER_SAY_HELLO, fh->sender_uid, 0);
00559 break;
00560 case QQ_FILE_CMD_SENDER_SAY_HELLO_ACK:
00561
00562 break;
00563 case QQ_FILE_CMD_RECEIVER_SAY_HELLO:
00564
00565 cursor += 47;
00566 read_packet_b(decrypted_data, &cursor,
00567 decrypted_len, &hellobyte);
00568 qq_send_file_ctl_packet(gc, QQ_FILE_CMD_RECEIVER_SAY_HELLO_ACK, fh->sender_uid, hellobyte);
00569 _qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP, QQ_FILE_BASIC_INFO, 0, 0, NULL, 0);
00570 break;
00571 case QQ_FILE_CMD_RECEIVER_SAY_HELLO_ACK:
00572
00573 break;
00574 case QQ_FILE_CMD_PING:
00575
00576 qq_send_file_ctl_packet(gc, QQ_FILE_CMD_PONG, fh->sender_uid, 0);
00577 break;
00578 case QQ_FILE_CMD_PONG:
00579 qq_send_file_ctl_packet(gc, QQ_FILE_CMD_SENDER_SAY_HELLO, fh->sender_uid, 0);
00580 break;
00581 default:
00582 gaim_debug(GAIM_DEBUG_INFO, "QQ", "unprocess file command %d\n", packet_type);
00583 }
00584 }
00585 g_free(md5);
00586 }
00587
00588 void
00589 _qq_recv_file_progess(GaimConnection *gc, guint8 *buffer, guint16 len, guint32 index, guint32 offset)
00590 {
00591 qq_data *qd = (qq_data *) gc->proto_data;
00592 GaimXfer *xfer = qd->xfer;
00593 ft_info *info = (ft_info *) xfer->data;
00594 guint32 mask;
00595
00596 gaim_debug(GAIM_DEBUG_INFO, "QQ", "receiving %dth fragment with length %d, slide window status %o, max_fragment_index %d\n",
00597 index, len, info->window, info->max_fragment_index);
00598 if (info->window == 0 && info->max_fragment_index == 0)
00599 {
00600 if (_qq_xfer_open_file(gaim_xfer_get_local_filename(xfer), "wb", xfer) == -1) {
00601 gaim_xfer_cancel_local(xfer);
00602 return;
00603 }
00604 gaim_debug(GAIM_DEBUG_INFO, "QQ", "object file opened for writing\n");
00605 }
00606 mask = 0x1 << (index % sizeof(info->window));
00607 if (index < info->max_fragment_index || (info->window & mask)) {
00608 gaim_debug(GAIM_DEBUG_INFO, "QQ", "duplicate %dth fragment, drop it!\n", index+1);
00609 return;
00610 }
00611
00612 info->window |= mask;
00613
00614 _qq_xfer_write_file(buffer, index, len, xfer);
00615
00616 xfer->bytes_sent += len;
00617 xfer->bytes_remaining -= len;
00618 gaim_xfer_update_progress(xfer);
00619
00620 mask = 0x1 << (info->max_fragment_index % sizeof(info->window));
00621 while (info->window & mask)
00622 {
00623 info->window &= ~mask;
00624 info->max_fragment_index ++;
00625 if (mask & 0x8000) mask = 0x0001;
00626 else mask = mask << 1;
00627 }
00628 gaim_debug(GAIM_DEBUG_INFO, "QQ", "procceed %dth fragment, slide window status %o, max_fragment_index %d\n",
00629 index, info->window, info->max_fragment_index);
00630 }
00631
00632 void
00633 _qq_send_file_progess(GaimConnection *gc)
00634 {
00635 qq_data *qd = (qq_data *) gc->proto_data;
00636 GaimXfer *xfer = qd->xfer;
00637 ft_info *info = (ft_info *) xfer->data;
00638 guint32 mask;
00639 guint8 *buffer;
00640 guint i;
00641 gint readbytes;
00642
00643 if (gaim_xfer_get_bytes_remaining(xfer) <= 0) return;
00644 if (info->window == 0 && info->max_fragment_index == 0)
00645 {
00646 if (_qq_xfer_open_file(gaim_xfer_get_local_filename(xfer), "rb", xfer) == -1) {
00647 gaim_xfer_cancel_local(xfer);
00648 return;
00649 }
00650 }
00651 buffer = g_newa(guint8, info->fragment_len);
00652 mask = 0x1 << (info->max_fragment_index % sizeof(info->window));
00653 for (i = 0; i < sizeof(info->window); i++) {
00654 if ((info->window & mask) == 0) {
00655 readbytes = _qq_xfer_read_file(buffer, info->max_fragment_index + i, info->fragment_len, xfer);
00656 if (readbytes > 0)
00657 _qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP, QQ_FILE_DATA_INFO,
00658 info->max_fragment_index + i + 1, 0, buffer, readbytes);
00659 }
00660 if (mask & 0x8000) mask = 0x0001;
00661 else mask = mask << 1;
00662 }
00663 }
00664
00665 void
00666 _qq_update_send_progess(GaimConnection *gc, guint32 fragment_index)
00667 {
00668 qq_data *qd = (qq_data *) gc->proto_data;
00669 GaimXfer *xfer = qd->xfer;
00670 ft_info *info = (ft_info *) xfer->data;
00671 guint32 mask;
00672
00673 gaim_debug(GAIM_DEBUG_INFO, "QQ", "receiving %dth fragment ack, slide window status %o, max_fragment_index %d\n",
00674 fragment_index, info->window, info->max_fragment_index);
00675 if (fragment_index < info->max_fragment_index ||
00676 fragment_index >= info->max_fragment_index + sizeof(info->window)) {
00677 gaim_debug(GAIM_DEBUG_INFO, "QQ", "duplicate %dth fragment, drop it!\n", fragment_index+1);
00678 return;
00679 }
00680 mask = 0x1 << (fragment_index % sizeof(info->window));
00681 if ((info->window & mask) == 0)
00682 {
00683 info->window |= mask;
00684 if (fragment_index + 1 != info->fragment_num) {
00685 xfer->bytes_sent += info->fragment_len;
00686 } else {
00687 xfer->bytes_sent += gaim_xfer_get_size(xfer) % info->fragment_len;
00688 }
00689 xfer->bytes_remaining = gaim_xfer_get_size(xfer) - gaim_xfer_get_bytes_sent(xfer);
00690 gaim_xfer_update_progress(xfer);
00691 if (gaim_xfer_get_bytes_remaining(xfer) <= 0) {
00692
00693 gaim_xfer_set_completed(xfer, TRUE);
00694 return;
00695 }
00696 mask = 0x1 << (info->max_fragment_index % sizeof(info->window));
00697 while (info->window & mask)
00698 {
00699
00700 info->window &= ~mask;
00701 guint8 *buffer;
00702 gint readbytes;
00703
00704 buffer = g_newa(guint8, info->fragment_len);
00705 readbytes = _qq_xfer_read_file(buffer, info->max_fragment_index + sizeof(info->window),
00706 info->fragment_len, xfer);
00707 if (readbytes > 0)
00708 _qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP, QQ_FILE_DATA_INFO,
00709 info->max_fragment_index + sizeof(info->window) + 1, 0, buffer, readbytes);
00710
00711 info->max_fragment_index ++;
00712 if (mask & 0x8000) mask = 0x0001;
00713 else mask = mask << 1;
00714 }
00715 }
00716 gaim_debug(GAIM_DEBUG_INFO, "QQ", "procceed %dth fragment ack, slide window status %o, max_fragment_index %d\n",
00717 fragment_index, info->window, info->max_fragment_index);
00718 }
00719
00720 void
00721 _qq_process_recv_file_data(GaimConnection *gc, guint8 *data, guint8 *cursor,
00722 gint len, guint32 to_uid)
00723 {
00724 guint16 packet_type;
00725 guint16 packet_seq;
00726 guint8 sub_type;
00727 guint32 fragment_index;
00728 guint16 fragment_len;
00729 guint32 fragment_offset;
00730 qq_data *qd = (qq_data *) gc->proto_data;
00731 ft_info *info = (ft_info *) qd->xfer->data;
00732
00733 cursor += 1;
00734 read_packet_w(data, &cursor, len, &packet_type);
00735 switch(packet_type)
00736 {
00737 case QQ_FILE_CMD_FILE_OP:
00738 read_packet_w(data, &cursor, len, &packet_seq);
00739 read_packet_b(data, &cursor, len, &sub_type);
00740 switch (sub_type)
00741 {
00742 case QQ_FILE_BASIC_INFO:
00743 cursor += 4;
00744 read_packet_dw(data, &cursor, len, &info->fragment_num);
00745 read_packet_dw(data, &cursor, len, &info->fragment_len);
00746
00747
00748
00749
00750
00751 info->max_fragment_index = 0;
00752 info->window = 0;
00753 gaim_debug(GAIM_DEBUG_INFO, "QQ", "start receiving data, %d fragments with %d length each\n",
00754 info->fragment_num, info->fragment_len);
00755 _qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP_ACK, sub_type,
00756 0, 0, NULL, 0);
00757 break;
00758 case QQ_FILE_DATA_INFO:
00759 read_packet_dw(data, &cursor, len, &fragment_index);
00760 read_packet_dw(data, &cursor, len, &fragment_offset);
00761 read_packet_w(data, &cursor, len, &fragment_len);
00762 gaim_debug(GAIM_DEBUG_INFO, "QQ", "received %dth fragment with length %d, offset %d\n",
00763 fragment_index, fragment_len, fragment_offset);
00764
00765 _qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP_ACK, sub_type,
00766 fragment_index, packet_seq, NULL, 0);
00767 _qq_recv_file_progess(gc, cursor, fragment_len, fragment_index, fragment_offset);
00768 break;
00769 case QQ_FILE_EOF:
00770 gaim_debug(GAIM_DEBUG_INFO, "QQ", "end of receiving\n");
00771 _qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP_ACK, sub_type,
00772 0, 0, NULL, 0);
00773 break;
00774 }
00775 break;
00776 case QQ_FILE_CMD_FILE_OP_ACK:
00777 read_packet_w(data, &cursor, len, &packet_seq);
00778 read_packet_b(data, &cursor, len, &sub_type);
00779 switch (sub_type)
00780 {
00781 case QQ_FILE_BASIC_INFO:
00782 info->max_fragment_index = 0;
00783 info->window = 0;
00784
00785 _qq_send_file_progess(gc);
00786 break;
00787 case QQ_FILE_DATA_INFO:
00788 read_packet_dw(data, &cursor, len, &fragment_index);
00789 _qq_update_send_progess(gc, fragment_index);
00790 if (gaim_xfer_is_completed(qd->xfer))
00791 _qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP, QQ_FILE_EOF, 0, 0, NULL, 0);
00792
00793
00794 break;
00795 case QQ_FILE_EOF:
00796
00797
00798 _qq_send_file_data_packet(gc, QQ_FILE_EOF, 0, 0, 0, NULL, 0);
00799 gaim_xfer_set_completed(qd->xfer, TRUE);
00800 break;
00801 }
00802 break;
00803 case QQ_FILE_EOF:
00804 _qq_send_file_data_packet(gc, QQ_FILE_EOF, 0, 0, 0, NULL, 0);
00805 gaim_xfer_set_completed(qd->xfer, TRUE);
00806 gaim_xfer_end(qd->xfer);
00807 break;
00808 case QQ_FILE_BASIC_INFO:
00809 gaim_debug(GAIM_DEBUG_INFO, "QQ", "here\n");
00810 _qq_send_file_data_packet(gc, QQ_FILE_DATA_INFO, 0, 0, 0, NULL, 0);
00811 break;
00812 default:
00813 gaim_debug(GAIM_DEBUG_INFO, "QQ", "_qq_process_recv_file_data: unknown packet type [%d]\n",
00814 packet_type);
00815 break;
00816 }
00817 }
00818
00819 void qq_process_recv_file(GaimConnection *gc, guint8 *data, gint len)
00820 {
00821 guint8 *cursor;
00822 qq_file_header fh;
00823 qq_data *qd;
00824
00825 g_return_if_fail(gc != NULL && gc->proto_data != NULL);
00826 qd = (qq_data *) gc->proto_data;
00827
00828 cursor = data;
00829 _qq_get_file_header(data, &cursor, len, &fh);
00830
00831 switch (fh.tag) {
00832 case QQ_FILE_CONTROL_PACKET_TAG:
00833 _qq_process_recv_file_ctl_packet(gc, data, cursor, len, &fh);
00834 break;
00835 case QQ_FILE_DATA_PACKET_TAG:
00836 _qq_process_recv_file_data(gc, data, cursor, len, fh.sender_uid);
00837 break;
00838 default:
00839 gaim_debug(GAIM_DEBUG_INFO, "QQ", "unknown packet tag");
00840 }
00841 }
00842