首页 | 数据结构 | 文件列表 | 数据字段 | 全局定义

file_trans.c

浏览该文件的文档。
00001 
00025 #ifdef _WIN32
00026 #define random rand
00027 #endif
00028 
00029 #include "debug.h"              // gaim_debug
00030 #include "ft.h"                 // gaim_xfer
00031 #include "md5.h"
00032 
00033 #include "file_trans.h"
00034 #include "send_file.h"  // ft_info
00035 #include "packet_parse.h"       //read_packet
00036 #include "qq.h"
00037 #include "header_info.h"
00038 #include "im.h"         //gen_session_md5
00039 #include "crypt.h"              //qq_crypt
00040 #include "proxy.h"              //qq_proxy_write
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 /* The memmap version has better performance for big files transfering
00160  * but it will spend plenty of memory, so do not use it in a low-memory host
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                 //gaim_xfer_write(qd->xfer, buf, bytes);
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 // send a file to udp channel with QQ_FILE_CONTROL_PACKET_TAG
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                 //debug: try to decrypt it
00359                 /*
00360                 if (QQ_DEBUG) {
00361                         gaim_debug(GAIM_DEBUG_INFO, "QQ", "encrypted packet: \n%s",
00362                                 hex_dump_to_str(encrypted_data, encrypted_len));
00363                         guint8 *buf;
00364                         int buflen;
00365                         buf = g_newa(guint8, MAX_PACKET_SIZE);
00366                         buflen = encrypted_len;
00367                         if (qq_crypt(DECRYPT, encrypted_data, encrypted_len, info->file_session_key, buf, &buflen)) {
00368                                 gaim_debug(GAIM_DEBUG_INFO, "QQ", "decrypt success\n");
00369                                 if (buflen == bytes && memcmp(raw_data, buf, buflen) == 0)
00370                                         gaim_debug(GAIM_DEBUG_INFO, "QQ", "checksum ok\n");
00371                                 gaim_debug(GAIM_DEBUG_INFO, "QQ", "decrypted packet: \n%s",
00372                                         hex_dump_to_str(buf, buflen));
00373                         } else {
00374                                 gaim_debug(GAIM_DEBUG_INFO, "QQ", "decrypt fail\n");
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 // send a file to udp channel with QQ_FILE_DATA_PACKET_TAG
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                                         /* Unknown */
00437                                         bytes += create_packet_w(raw_data, &cursor, 0x0000);
00438                                         /* Sub-operation type */
00439                                         bytes += create_packet_b(raw_data, &cursor, sub_type);
00440                                         /* Length of file */
00441                                         bytes += create_packet_dw(raw_data, &cursor, filesize);
00442                                         /* Number of fragments */
00443                                         bytes += create_packet_dw(raw_data, &cursor, info->fragment_num);
00444                                         /* Length of a single fragment */
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                                         /* Length of filename */
00449                                         bytes += create_packet_w(raw_data, &cursor, filename_len);
00450                                         /* 8 unknown bytes */
00451                                         bytes += create_packet_dw(raw_data, &cursor, 0x00000000);
00452                                         bytes += create_packet_dw(raw_data, &cursor, 0x00000000);
00453                                         /* filename */
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                                         //bytes += create_packet_w(raw_data, &cursor, ++(qd->send_seq));
00461                                         bytes += create_packet_w(raw_data, &cursor, info->send_seq);
00462                                         bytes += create_packet_b(raw_data, &cursor, sub_type);
00463                                         //bytes += create_packet_dw(raw_data, &cursor, fragment_index);
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                                         //bytes += create_packet_w(raw_data, &cursor, info->fragment_num + 1);
00472                                         bytes += create_packet_w(raw_data, &cursor, info->fragment_num);
00473                                         bytes += create_packet_b(raw_data, &cursor, sub_type);
00474                                         //gaim_xfer_set_completed(qd->xfer, TRUE);
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 /* An conversation starts like this
00501  * Sender ==> Receiver [QQ_FILE_CMD_PING]
00502  * Sender <== Receiver [QQ_FILE_CMD_PONG]
00503  * Sender ==> Receiver [QQ_FILE_CMD_SENDER_SAY_HELLO]
00504  * Sender <== Receiver [QQ_FILE_CMD_SENDER_SAY_HELLO_ACK]
00505  * Sender <== Receiver [QQ_FILE_CMD_RECEIVER_SAY_HELLO]
00506  * Sender ==> Receiver [QQ_FILE_CMD_RECEIVER_SAY_HELLO_ACK]
00507  * Sender ==> Receiver [QQ_FILE_CMD_FILE_OP, QQ_FILE_BASIC_INFO]
00508  * Sender <== Receiver [QQ_FILE_CMD_FILE_OP_ACK, QQ_FILE_BASIC_INFO]
00509  * Sender ==> Receiver [QQ_FILE_CMD_FILE_OP, QQ_FILE_DATA_INFO]
00510  * Sender <== Receiver [QQ_FILE_CMD_FILE_OP_ACK, QQ_FILE_DATA_INFO]
00511  * Sender ==> Receiver [QQ_FILE_CMD_FILE_OP, QQ_FILE_DATA_INFO]
00512  * Sender <== Receiver [QQ_FILE_CMD_FILE_OP_ACK, QQ_FILE_DATA_INFO]
00513  * ......
00514  * Sender ==> Receiver [QQ_FILE_CMD_FILE_OP, QQ_FILE_EOF]
00515  * Sender <== Receiver [QQ_FILE_CMD_FILE_OP_ACK, QQ_FILE_EOF]
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;   //skip md5 section
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 //                              qq_send_file_ctl_packet(gc, QQ_FILE_CMD_PING, fh->sender_uid, 0);
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                                 /* I'm receiver, if we receive SAY_HELLO from sender, we send back the ACK */
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                                 /* I'm sender, do nothing */
00562                                 break;
00563                         case QQ_FILE_CMD_RECEIVER_SAY_HELLO:
00564                                 /* I'm sender, ack the hello packet and send the first data */
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                                 /* I'm receiver, do nothing */
00573                                 break;
00574                         case QQ_FILE_CMD_PING:
00575                                 /* I'm receiver, ack the PING */
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                         /* We have finished sending the file */
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                         //move the slide window
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;//skip an unknown byte
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;    //file length, we have already known it from xfer
00744                                         read_packet_dw(data, &cursor, len, &info->fragment_num);
00745                                         read_packet_dw(data, &cursor, len, &info->fragment_len);
00746 
00747                                         /* FIXME: We must check the md5 here, if md5 doesn't match
00748                                          * we will ignore the packet or send sth as error number
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                                         /* It is ready to send file data */
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                                 //      else
00793                                 //              _qq_send_file_progess(gc);
00794                                         break;
00795                                 case QQ_FILE_EOF:
00796                                         /* FIXME: OK, we can end the connection successfully */
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 

Generated at Mon May 8 15:41:23 2006 for OpenQ by  doxygen 1.4.4