| |
1 /** |
| |
2 * @file file_trans.c |
| |
3 * |
| |
4 * purple |
| |
5 * |
| |
6 * Purple is the legal property of its developers, whose names are too numerous |
| |
7 * to list here. Please refer to the COPYRIGHT file distributed with this |
| |
8 * source distribution. |
| |
9 * |
| |
10 * This program is free software; you can redistribute it and/or modify |
| |
11 * it under the terms of the GNU General Public License as published by |
| |
12 * the Free Software Foundation; either version 2 of the License, or |
| |
13 * (at your option) any later version. |
| |
14 * |
| |
15 * This program is distributed in the hope that it will be useful, |
| |
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| |
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| |
18 * GNU General Public License for more details. |
| |
19 * |
| |
20 * You should have received a copy of the GNU General Public License |
| |
21 * along with this program; if not, write to the Free Software |
| |
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| |
23 */ |
| |
24 |
| |
25 #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 PurpleCipher *cipher; |
| |
81 PurpleCipherContext *context; |
| |
82 |
| |
83 g_return_if_fail(filename != NULL && md5 != NULL); |
| |
84 |
| |
85 cipher = purple_ciphers_find_cipher("md5"); |
| |
86 context = purple_cipher_context_new(cipher, NULL); |
| |
87 purple_cipher_context_append(context, (guint8 *) filename, strlen(filename)); |
| |
88 purple_cipher_context_digest(context, 16, md5, NULL); |
| |
89 purple_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 PurpleCipher *cipher; |
| |
97 PurpleCipherContext *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 = purple_ciphers_find_cipher("md5"); |
| |
113 context = purple_cipher_context_new(cipher, NULL); |
| |
114 purple_cipher_context_append(context, buffer, filelen); |
| |
115 purple_cipher_context_digest(context, 16, md5, NULL); |
| |
116 purple_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, PurpleXfer *xfer) |
| |
173 { |
| |
174 ft_info *info = xfer->data; |
| |
175 int fd; |
| |
176 if (method[0] == 'r') { |
| |
177 fd = open(purple_xfer_get_local_filename(xfer), O_RDONLY); |
| |
178 info->buffer = mmap(0, purple_xfer_get_size(xfer), PROT_READ, MAP_PRIVATE, fd, 0); |
| |
179 } |
| |
180 else |
| |
181 { |
| |
182 fd = open(purple_xfer_get_local_filename(xfer), O_RDWR|O_CREAT, 0644); |
| |
183 info->buffer = mmap(0, purple_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, PurpleXfer *xfer) |
| |
193 { |
| |
194 ft_info *info = xfer->data; |
| |
195 gint readbytes; |
| |
196 |
| |
197 buffer = info->buffer + len * index; |
| |
198 readbytes = purple_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, PurpleXfer *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(PurpleXfer *xfer) |
| |
212 { |
| |
213 ft_info *info = xfer->data; |
| |
214 |
| |
215 if (info->buffer) munmap(info->buffer, purple_xfer_get_size(xfer)); |
| |
216 } |
| |
217 #else |
| |
218 static int _qq_xfer_open_file(const gchar *filename, const gchar *method, PurpleXfer *xfer) |
| |
219 { |
| |
220 ft_info *info = xfer->data; |
| |
221 info->dest_fp = fopen(purple_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, PurpleXfer *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, PurpleXfer *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(PurpleXfer *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(PurpleConnection *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 purple_debug(PURPLE_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(PurpleConnection *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 purple_debug(PURPLE_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 purple_debug(PURPLE_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 purple_debug(PURPLE_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 purple_debug(PURPLE_DEBUG_INFO, "QQ", "decrypt success\n"); |
| |
365 if (buflen == bytes && memcmp(raw_data, buf, buflen) == 0) |
| |
366 purple_debug(PURPLE_DEBUG_INFO, "QQ", "checksum ok\n"); |
| |
367 hex_dump = hex_dump_to_str(buf, buflen); |
| |
368 purple_debug(PURPLE_DEBUG_INFO, "QQ", "decrypted packet: \n%s", hex_dump); |
| |
369 g_free(hex_dump); |
| |
370 } else { |
| |
371 purple_debug(PURPLE_DEBUG_INFO, "QQ", "decrypt fail\n"); |
| |
372 } |
| |
373 } |
| |
374 */ |
| |
375 |
| |
376 purple_debug(PURPLE_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 purple_debug(PURPLE_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(PurpleConnection *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 *) purple_xfer_get_filename(qd->xfer); |
| |
400 filesize = purple_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(purple_xfer_get_local_filename(qd->xfer), |
| |
422 purple_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 purple_debug(PURPLE_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 purple_debug(PURPLE_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 purple_debug(PURPLE_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 /* purple_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 purple_debug(PURPLE_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(PurpleConnection *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 purple_debug(PURPLE_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 purple_debug(PURPLE_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 purple_debug(PURPLE_DEBUG_INFO, "QQ", "unprocess file command %d\n", packet_type); |
| |
578 } |
| |
579 } |
| |
580 } |
| |
581 |
| |
582 static void _qq_recv_file_progess(PurpleConnection *gc, guint8 *buffer, guint16 len, guint32 index, guint32 offset) |
| |
583 { |
| |
584 qq_data *qd = (qq_data *) gc->proto_data; |
| |
585 PurpleXfer *xfer = qd->xfer; |
| |
586 ft_info *info = (ft_info *) xfer->data; |
| |
587 guint32 mask; |
| |
588 |
| |
589 purple_debug(PURPLE_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(purple_xfer_get_local_filename(xfer), "wb", xfer) == -1) { |
| |
594 purple_xfer_cancel_local(xfer); |
| |
595 return; |
| |
596 } |
| |
597 purple_debug(PURPLE_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 purple_debug(PURPLE_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 purple_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 purple_debug(PURPLE_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(PurpleConnection *gc) |
| |
626 { |
| |
627 qq_data *qd = (qq_data *) gc->proto_data; |
| |
628 PurpleXfer *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 (purple_xfer_get_bytes_remaining(xfer) <= 0) return; |
| |
636 if (info->window == 0 && info->max_fragment_index == 0) |
| |
637 { |
| |
638 if (_qq_xfer_open_file(purple_xfer_get_local_filename(xfer), "rb", xfer) == -1) { |
| |
639 purple_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(PurpleConnection *gc, guint32 fragment_index) |
| |
658 { |
| |
659 guint32 mask; |
| |
660 guint8 *buffer; |
| |
661 gint readbytes; |
| |
662 qq_data *qd = (qq_data *) gc->proto_data; |
| |
663 PurpleXfer *xfer = qd->xfer; |
| |
664 ft_info *info = (ft_info *) xfer->data; |
| |
665 |
| |
666 purple_debug(PURPLE_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 purple_debug(PURPLE_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 += purple_xfer_get_size(xfer) % info->fragment_len; |
| |
682 } |
| |
683 xfer->bytes_remaining = purple_xfer_get_size(xfer) - purple_xfer_get_bytes_sent(xfer); |
| |
684 purple_xfer_update_progress(xfer); |
| |
685 if (purple_xfer_get_bytes_remaining(xfer) <= 0) { |
| |
686 /* We have finished sending the file */ |
| |
687 purple_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 purple_debug(PURPLE_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(PurpleConnection *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 purple_debug(PURPLE_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 purple_debug(PURPLE_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 purple_debug(PURPLE_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 (purple_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 purple_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 purple_xfer_set_completed(qd->xfer, TRUE); |
| |
800 purple_xfer_end(qd->xfer); |
| |
801 break; |
| |
802 case QQ_FILE_BASIC_INFO: |
| |
803 purple_debug(PURPLE_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 purple_debug(PURPLE_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(PurpleConnection *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 purple_debug(PURPLE_DEBUG_INFO, "QQ", "unknown packet tag"); |
| |
833 } |
| |
834 } |