Annotation of ChivanetAimPidgin/oscarprpl/src/c/oft.c, revision 1.1.1.1
1.1 snw 1: /*
2: * Purple's oscar protocol plugin
3: * This file is the legal property of its developers.
4: * Please see the AUTHORS file distributed alongside this file.
5: *
6: * This library is free software; you can redistribute it and/or
7: * modify it under the terms of the GNU Lesser General Public
8: * License as published by the Free Software Foundation; either
9: * version 2 of the License, or (at your option) any later version.
10: *
11: * This library is distributed in the hope that it will be useful,
12: * but WITHOUT ANY WARRANTY; without even the implied warranty of
13: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14: * Lesser General Public License for more details.
15: *
16: * You should have received a copy of the GNU Lesser General Public
17: * License along with this library; if not, write to the Free Software
18: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
19: */
20:
21: /*
22: * I feel like this is a good place to explain OFT, so I'm going to
23: * do just that. Each OFT packet has a header type. I guess this
24: * is pretty similar to the subtype of a SNAC packet. The type
25: * basically tells the other client the meaning of the OFT packet.
26: * There are two distinct types of file transfer, which I usually
27: * call "sendfile" and "getfile." Sendfile is when you send a file
28: * to another AIM user. Getfile is when you share a group of files,
29: * and other users request that you send them the files.
30: *
31: * A typical sendfile file transfer goes like this:
32: * 1) Sender sends a channel 2 ICBM telling the other user that
33: * we want to send them a file. At the same time, we open a
34: * listener socket (this should be done before sending the
35: * ICBM) on some port, and wait for them to connect to us.
36: * The ICBM we sent should contain our IP address and the port
37: * number that we're listening on.
38: * 2) The receiver connects to the sender on the given IP address
39: * and port. After the connection is established, the receiver
40: * sends an ICBM signifying that we are ready and waiting.
41: * 3) The sender sends an OFT PROMPT message over the OFT
42: * connection.
43: * 4) The receiver of the file sends back an exact copy of this
44: * OFT packet, except the cookie is filled in with the cookie
45: * from the ICBM. I think this might be an attempt to verify
46: * that the user that is connected is actually the guy that
47: * we sent the ICBM to. Oh, I've been calling this the ACK.
48: * 5) The sender starts sending raw data across the connection
49: * until the entire file has been sent.
50: * 6) The receiver knows the file is finished because the sender
51: * sent the file size in an earlier OFT packet. So then the
52: * receiver sends the DONE thingy (after filling in the
53: * "received" checksum and size) and closes the connection.
54: */
55:
56: #include "oscar.h"
57: #include "peer.h"
58: #include <glib/gstdio.h>
59: #include "util.h"
60:
61: #define CHECKSUM_BUFFER_SIZE 256 * 1024
62:
63: struct _ChecksumData
64: {
65: PeerConnection *conn;
66: PurpleXfer *xfer;
67: GSourceFunc callback;
68: size_t size;
69: guint32 checksum;
70: size_t total;
71: FILE *file;
72: guint8 buffer[CHECKSUM_BUFFER_SIZE];
73: guint timer;
74: };
75:
76: void
77: peer_oft_checksum_destroy(ChecksumData *checksum_data)
78: {
79: checksum_data->conn->checksum_data = NULL;
80: fclose(checksum_data->file);
81: if (checksum_data->timer > 0)
82: purple_timeout_remove(checksum_data->timer);
83: g_free(checksum_data);
84: }
85:
86: /**
87: * Calculate oft checksum of buffer
88: *
89: * Prevcheck should be 0xFFFF0000 when starting a checksum of a file. The
90: * checksum is kind of a rolling checksum thing, so each time you get bytes
91: * of a file you just call this puppy and it updates the checksum. You can
92: * calculate the checksum of an entire file by calling this in a while or a
93: * for loop, or something.
94: *
95: * Thanks to Graham Booker for providing this improved checksum routine,
96: * which is simpler and should be more accurate than Josh Myer's original
97: * code. -- wtm
98: *
99: * This algorithm works every time I have tried it. The other fails
100: * sometimes. So, AOL who thought this up? It has got to be the weirdest
101: * checksum I have ever seen.
102: *
103: * @param buffer Buffer of data to checksum. Man I'd like to buff her...
104: * @param bufsize Size of buffer.
105: * @param prevchecksum Previous checksum.
106: * @param odd Whether an odd number of bytes have been processed before this call
107: */
108: static guint32
109: peer_oft_checksum_chunk(const guint8 *buffer, int bufferlen, guint32 prevchecksum, int odd)
110: {
111: guint32 checksum, oldchecksum;
112: int i = 0;
113: unsigned short val;
114:
115: checksum = (prevchecksum >> 16) & 0xffff;
116: if (odd)
117: {
118: /*
119: * This is one hell of a hack, but it should always work.
120: * Essentially, I am reindexing the array so that index 1
121: * is the first element. Since the odd and even bytes are
122: * detected by the index number.
123: */
124: i = 1;
125: bufferlen++;
126: buffer--;
127: }
128: for (; i < bufferlen; i++)
129: {
130: oldchecksum = checksum;
131: if (i & 1)
132: val = buffer[i];
133: else
134: val = buffer[i] << 8;
135: checksum -= val;
136: /*
137: * The following appears to be necessary.... It happens
138: * every once in a while and the checksum doesn't fail.
139: */
140: if (checksum > oldchecksum)
141: checksum--;
142: }
143: checksum = ((checksum & 0x0000ffff) + (checksum >> 16));
144: checksum = ((checksum & 0x0000ffff) + (checksum >> 16));
145: return checksum << 16;
146: }
147:
148: static gboolean
149: peer_oft_checksum_file_piece(gpointer data)
150: {
151: ChecksumData *checksum_data;
152: gboolean repeat;
153:
154: checksum_data = data;
155: repeat = FALSE;
156:
157: if (checksum_data->total < checksum_data->size)
158: {
159: size_t bytes = MIN(CHECKSUM_BUFFER_SIZE,
160: checksum_data->size - checksum_data->total);
161:
162: bytes = fread(checksum_data->buffer, 1, bytes, checksum_data->file);
163: if (bytes != 0)
164: {
165: checksum_data->checksum = peer_oft_checksum_chunk(checksum_data->buffer, bytes, checksum_data->checksum, checksum_data->total & 1);
166: checksum_data->total += bytes;
167: repeat = TRUE;
168: }
169: }
170:
171: if (!repeat)
172: {
173: purple_debug_info("oscar", "Checksum of %s calculated\n",
174: purple_xfer_get_local_filename(checksum_data->xfer));
175: if (checksum_data->callback != NULL)
176: checksum_data->callback(checksum_data);
177: peer_oft_checksum_destroy(checksum_data);
178: }
179:
180: return repeat;
181: }
182:
183: /**
184: * Calculate oft checksum of a file in a series of calls to
185: * peer_oft_checksum_file_piece(). We do it this way because
186: * calculating the checksum on large files can take a long time,
187: * and we want to return control to the UI so that the application
188: * doesn't appear completely frozen.
189: *
190: * @param conn The connection used for this file transfer.
191: * @param xfer The file transfer needing this checksum.
192: * @param callback The function to call upon calculation of the checksum.
193: * @param size The maximum size to check.
194: */
195:
196: static void
197: peer_oft_checksum_file(PeerConnection *conn, PurpleXfer *xfer, GSourceFunc callback, size_t size)
198: {
199: ChecksumData *checksum_data;
200:
201: purple_debug_info("oscar", "Calculating checksum of %s\n",
202: purple_xfer_get_local_filename(xfer));
203:
204: checksum_data = g_malloc0(sizeof(ChecksumData));
205: checksum_data->conn = conn;
206: checksum_data->xfer = xfer;
207: checksum_data->callback = callback;
208: checksum_data->size = size;
209: checksum_data->checksum = 0xffff0000;
210: checksum_data->file = g_fopen(purple_xfer_get_local_filename(xfer), "rb");
211:
212: if (checksum_data->file == NULL)
213: {
214: purple_debug_error("oscar", "Unable to open %s for checksumming: %s\n",
215: purple_xfer_get_local_filename(xfer), g_strerror(errno));
216: callback(checksum_data);
217: g_free(checksum_data);
218: }
219: else
220: {
221: checksum_data->timer = purple_timeout_add(10,
222: peer_oft_checksum_file_piece, checksum_data);
223: conn->checksum_data = checksum_data;
224: }
225: }
226:
227: static void
228: peer_oft_copy_xfer_data(PeerConnection *conn, OftFrame *frame)
229: {
230: g_free(conn->xferdata.name);
231:
232: memcpy(&(conn->xferdata), frame, sizeof(OftFrame));
233: conn->xferdata.name = g_memdup(frame->name, frame->name_length);
234: }
235:
236: /**
237: * Free any OFT related data.
238: */
239: void
240: peer_oft_close(PeerConnection *conn)
241: {
242: /*
243: * If cancelled by local user, and we're receiving a file, and
244: * we're not connected/ready then send an ICBM cancel message.
245: */
246: if ((purple_xfer_get_status(conn->xfer) == PURPLE_XFER_STATUS_CANCEL_LOCAL) &&
247: !conn->ready)
248: {
249: aim_im_sendch2_cancel(conn);
250: }
251:
252: if (conn->sending_data_timer != 0)
253: {
254: purple_timeout_remove(conn->sending_data_timer);
255: conn->sending_data_timer = 0;
256: }
257: }
258:
259: /**
260: * Write the given OftFrame to a ByteStream and send it out
261: * on the established PeerConnection.
262: */
263: static void
264: peer_oft_send(PeerConnection *conn, OftFrame *frame)
265: {
266: size_t length;
267: ByteStream bs;
268:
269: length = 192 + frame->name_length;
270: byte_stream_new(&bs, length);
271: byte_stream_putraw(&bs, conn->magic, 4);
272: byte_stream_put16(&bs, length);
273: byte_stream_put16(&bs, frame->type);
274: byte_stream_putraw(&bs, frame->cookie, 8);
275: byte_stream_put16(&bs, frame->encrypt);
276: byte_stream_put16(&bs, frame->compress);
277: byte_stream_put16(&bs, frame->totfiles);
278: byte_stream_put16(&bs, frame->filesleft);
279: byte_stream_put16(&bs, frame->totparts);
280: byte_stream_put16(&bs, frame->partsleft);
281: byte_stream_put32(&bs, frame->totsize);
282: byte_stream_put32(&bs, frame->size);
283: byte_stream_put32(&bs, frame->modtime);
284: byte_stream_put32(&bs, frame->checksum);
285: byte_stream_put32(&bs, frame->rfrcsum);
286: byte_stream_put32(&bs, frame->rfsize);
287: byte_stream_put32(&bs, frame->cretime);
288: byte_stream_put32(&bs, frame->rfcsum);
289: byte_stream_put32(&bs, frame->nrecvd);
290: byte_stream_put32(&bs, frame->recvcsum);
291: byte_stream_putraw(&bs, frame->idstring, 32);
292: byte_stream_put8(&bs, frame->flags);
293: byte_stream_put8(&bs, frame->lnameoffset);
294: byte_stream_put8(&bs, frame->lsizeoffset);
295: byte_stream_putraw(&bs, frame->dummy, 69);
296: byte_stream_putraw(&bs, frame->macfileinfo, 16);
297: byte_stream_put16(&bs, frame->nencode);
298: byte_stream_put16(&bs, frame->nlanguage);
299: /*
300: * The name can be more than 64 characters, but if it is less than
301: * 64 characters it is padded with NULLs.
302: */
303: byte_stream_putraw(&bs, frame->name, frame->name_length);
304:
305: peer_connection_send(conn, &bs);
306:
307: byte_stream_destroy(&bs);
308: }
309:
310: void
311: peer_oft_send_prompt(PeerConnection *conn)
312: {
313: conn->xferdata.type = PEER_TYPE_PROMPT;
314: peer_oft_send(conn, &conn->xferdata);
315: }
316:
317: static void
318: peer_oft_send_ack(PeerConnection *conn)
319: {
320: conn->xferdata.type = PEER_TYPE_ACK;
321:
322: /* Fill in the cookie */
323: memcpy(conn->xferdata.cookie, conn->cookie, 8);
324:
325: peer_oft_send(conn, &conn->xferdata);
326: }
327:
328: static void
329: peer_oft_send_resume_accept(PeerConnection *conn)
330: {
331: conn->xferdata.type = PEER_TYPE_RESUMEACCEPT;
332:
333: /* Fill in the cookie */
334: memcpy(conn->xferdata.cookie, conn->cookie, 8);
335:
336: peer_oft_send(conn, &conn->xferdata);
337: }
338:
339: static void
340: peer_oft_send_done(PeerConnection *conn)
341: {
342: conn->xferdata.type = PEER_TYPE_DONE;
343: conn->xferdata.rfrcsum = 0xffff0000;
344: conn->xferdata.nrecvd = purple_xfer_get_bytes_sent(conn->xfer);
345: peer_oft_send(conn, &conn->xferdata);
346: }
347:
348: /**
349: * This function exists so that we don't remove the outgoing
350: * data watcher while we're still sending data. In most cases
351: * any data we're sending will be instantly wisked away to a TCP
352: * buffer maintained by our operating system... but we want to
353: * make sure the core doesn't start sending file data while
354: * we're still sending OFT frame data. That would be bad.
355: */
356: static gboolean
357: start_transfer_when_done_sending_data(gpointer data)
358: {
359: PeerConnection *conn;
360:
361: conn = data;
362:
363: if (purple_circ_buffer_get_max_read(conn->buffer_outgoing) == 0)
364: {
365: conn->sending_data_timer = 0;
366: conn->xfer->fd = conn->fd;
367: conn->fd = -1;
368: purple_xfer_start(conn->xfer, conn->xfer->fd, NULL, 0);
369: return FALSE;
370: }
371:
372: return TRUE;
373: }
374:
375: /**
376: * This function is similar to the above function, except instead
377: * of starting the xfer it will destroy the connection. This is
378: * used when you want to send one final message across the peer
379: * connection, and then close everything.
380: */
381: static gboolean
382: destroy_connection_when_done_sending_data(gpointer data)
383: {
384: PeerConnection *conn;
385:
386: conn = data;
387:
388: if (purple_circ_buffer_get_max_read(conn->buffer_outgoing) == 0)
389: {
390: conn->sending_data_timer = 0;
391: peer_connection_destroy(conn, conn->disconnect_reason, NULL);
392: return FALSE;
393: }
394:
395: return TRUE;
396: }
397:
398: /*
399: * This is called when a buddy sends us some file info. This happens when they
400: * are sending a file to you, and you have just established a connection to them.
401: * You should send them the exact same info except use the real cookie. We also
402: * get like totally ready to like, receive the file, kay?
403: */
404: static void
405: peer_oft_recv_frame_prompt(PeerConnection *conn, OftFrame *frame)
406: {
407: /* Record the file information and send an ack */
408: peer_oft_copy_xfer_data(conn, frame);
409: peer_oft_send_ack(conn);
410:
411: /* Remove our watchers and use the file transfer watchers in the core */
412: purple_input_remove(conn->watcher_incoming);
413: conn->watcher_incoming = 0;
414: conn->sending_data_timer = purple_timeout_add(100,
415: start_transfer_when_done_sending_data, conn);
416: }
417:
418: /**
419: * We are sending a file to someone else. They have just acknowledged our
420: * prompt, so we want to start sending data like there's no tomorrow.
421: */
422: static void
423: peer_oft_recv_frame_ack(PeerConnection *conn, OftFrame *frame)
424: {
425: if (memcmp(conn->cookie, frame->cookie, 8) != 0)
426: {
427: purple_debug_info("oscar", "Received an incorrect cookie. "
428: "Closing connection.\n");
429: peer_connection_destroy(conn, OSCAR_DISCONNECT_INVALID_DATA, NULL);
430: return;
431: }
432:
433: /* Remove our watchers and use the file transfer watchers in the core */
434: purple_input_remove(conn->watcher_incoming);
435: conn->watcher_incoming = 0;
436: conn->sending_data_timer = purple_timeout_add(100,
437: start_transfer_when_done_sending_data, conn);
438: }
439:
440: static gboolean
441: peer_oft_recv_frame_resume_checksum_calculated_cb(gpointer data)
442: {
443: ChecksumData *checksum_data;
444: PeerConnection *conn;
445:
446: checksum_data = data;
447: conn = checksum_data->conn;
448:
449: /* Check the checksums here. If not match, don't allow resume */
450: if (checksum_data->checksum != conn->xferdata.recvcsum || checksum_data->total != conn->xferdata.nrecvd)
451: {
452: /* Reset internal structure */
453: conn->xferdata.recvcsum = 0xffff0000;
454: conn->xferdata.rfrcsum = 0xffff0000;
455: conn->xferdata.nrecvd = 0;
456: }
457: else
458: /* Accept the change */
459: purple_xfer_set_bytes_sent(checksum_data->xfer, conn->xferdata.nrecvd);
460:
461: peer_oft_send_resume_accept(conn);
462:
463: return FALSE;
464: }
465:
466: /**
467: * We are sending a file to someone else. They have just acknowledged our
468: * prompt and are asking to resume, so we accept their resume and await
469: * a resume ack.
470: */
471: static void
472: peer_oft_recv_frame_resume(PeerConnection *conn, OftFrame *frame)
473: {
474: if (memcmp(conn->cookie, frame->cookie, 8) != 0)
475: {
476: purple_debug_info("oscar", "Received an incorrect cookie. "
477: "Closing connection.\n");
478: peer_connection_destroy(conn, OSCAR_DISCONNECT_INVALID_DATA, NULL);
479: return;
480: }
481:
482: /* Copy resume data into internal structure */
483: conn->xferdata.recvcsum = frame->recvcsum;
484: conn->xferdata.rfrcsum = frame->rfrcsum;
485: conn->xferdata.nrecvd = frame->nrecvd;
486:
487: peer_oft_checksum_file(conn, conn->xfer,
488: peer_oft_recv_frame_resume_checksum_calculated_cb,
489: frame->nrecvd);
490: }
491:
492: /*
493: * We just sent a file to someone. They said they got it and everything,
494: * so we can close our direct connection and what not.
495: */
496: static void
497: peer_oft_recv_frame_done(PeerConnection *conn, OftFrame *frame)
498: {
499: /*
500: * The core ft code sets the xfer to completed automatically if we've
501: * sent all bytes to the other user. But this function can be called
502: * even if we haven't sent all bytes to the other user (in the case
503: * where the user already has this file on their computer and the
504: * checksum matches).
505: */
506: if (!purple_xfer_is_completed(conn->xfer))
507: purple_xfer_set_completed(conn->xfer, TRUE);
508:
509: purple_input_remove(conn->watcher_incoming);
510: conn->watcher_incoming = 0;
511: conn->xfer->fd = conn->fd;
512: conn->fd = -1;
513: conn->disconnect_reason = OSCAR_DISCONNECT_DONE;
514: peer_connection_schedule_destroy(conn, conn->disconnect_reason, NULL);
515: }
516:
517: /**
518: * Handle an incoming OftFrame. If there is a payload associated
519: * with this frame, then we remove the old watcher and add the
520: * OFT watcher to read in the payload.
521: */
522: void
523: peer_oft_recv_frame(PeerConnection *conn, ByteStream *bs)
524: {
525: OftFrame frame;
526:
527: frame.type = byte_stream_get16(bs);
528: byte_stream_getrawbuf(bs, frame.cookie, 8);
529: frame.encrypt = byte_stream_get16(bs);
530: frame.compress = byte_stream_get16(bs);
531: frame.totfiles = byte_stream_get16(bs);
532: frame.filesleft = byte_stream_get16(bs);
533: frame.totparts = byte_stream_get16(bs);
534: frame.partsleft = byte_stream_get16(bs);
535: frame.totsize = byte_stream_get32(bs);
536: frame.size = byte_stream_get32(bs);
537: frame.modtime = byte_stream_get32(bs);
538: frame.checksum = byte_stream_get32(bs);
539: frame.rfrcsum = byte_stream_get32(bs);
540: frame.rfsize = byte_stream_get32(bs);
541: frame.cretime = byte_stream_get32(bs);
542: frame.rfcsum = byte_stream_get32(bs);
543: frame.nrecvd = byte_stream_get32(bs);
544: frame.recvcsum = byte_stream_get32(bs);
545: byte_stream_getrawbuf(bs, frame.idstring, 32);
546: frame.flags = byte_stream_get8(bs);
547: frame.lnameoffset = byte_stream_get8(bs);
548: frame.lsizeoffset = byte_stream_get8(bs);
549: byte_stream_getrawbuf(bs, frame.dummy, 69);
550: byte_stream_getrawbuf(bs, frame.macfileinfo, 16);
551: frame.nencode = byte_stream_get16(bs);
552: frame.nlanguage = byte_stream_get16(bs);
553: frame.name_length = bs->len - 186;
554: frame.name = byte_stream_getraw(bs, frame.name_length);
555:
556: purple_debug_info("oscar", "Incoming OFT frame from %s with "
557: "type=0x%04x\n", conn->bn, frame.type);
558:
559: /* TODOFT: peer_oft_dirconvert_fromstupid(frame->name); */
560:
561: switch(frame.type)
562: {
563: case PEER_TYPE_PROMPT:
564: peer_oft_recv_frame_prompt(conn, &frame);
565: break;
566: case PEER_TYPE_ACK:
567: case PEER_TYPE_RESUMEACK:
568: peer_oft_recv_frame_ack(conn, &frame);
569: break;
570: case PEER_TYPE_RESUME:
571: peer_oft_recv_frame_resume(conn, &frame);
572: break;
573: case PEER_TYPE_DONE:
574: peer_oft_recv_frame_done(conn, &frame);
575: break;
576: default:
577: break;
578: }
579:
580: g_free(frame.name);
581: }
582:
583: /*******************************************************************/
584: /* Begin PurpleXfer callbacks for use when receiving a file */
585: /*******************************************************************/
586:
587: void
588: peer_oft_recvcb_init(PurpleXfer *xfer)
589: {
590: PeerConnection *conn;
591:
592: conn = xfer->data;
593: conn->flags |= PEER_CONNECTION_FLAG_APPROVED;
594: peer_connection_trynext(conn);
595: }
596:
597: void
598: peer_oft_recvcb_end(PurpleXfer *xfer)
599: {
600: PeerConnection *conn;
601:
602: conn = xfer->data;
603:
604: /* Tell the other person that we've received everything */
605: conn->fd = conn->xfer->fd;
606: conn->xfer->fd = -1;
607: peer_oft_send_done(conn);
608:
609: conn->disconnect_reason = OSCAR_DISCONNECT_DONE;
610: conn->sending_data_timer = purple_timeout_add(100,
611: destroy_connection_when_done_sending_data, conn);
612: }
613:
614: void
615: peer_oft_recvcb_ack_recv(PurpleXfer *xfer, const guchar *buffer, size_t size)
616: {
617: PeerConnection *conn;
618:
619: /* Update our rolling checksum. Like Walmart, yo. */
620: conn = xfer->data;
621: conn->xferdata.recvcsum = peer_oft_checksum_chunk(buffer,
622: size, conn->xferdata.recvcsum, purple_xfer_get_bytes_sent(xfer) & 1);
623: }
624:
625: /*******************************************************************/
626: /* End PurpleXfer callbacks for use when receiving a file */
627: /*******************************************************************/
628:
629: /*******************************************************************/
630: /* Begin PurpleXfer callbacks for use when sending a file */
631: /*******************************************************************/
632:
633: static gboolean
634: peer_oft_checksum_calculated_cb(gpointer data)
635: {
636: ChecksumData *checksum_data;
637: PeerConnection *conn;
638:
639: checksum_data = data;
640: conn = checksum_data->conn;
641:
642: conn->xferdata.checksum = checksum_data->checksum;
643:
644: /* Start the connection process */
645: peer_connection_trynext(checksum_data->conn);
646:
647: return FALSE;
648: }
649:
650: void
651: peer_oft_sendcb_init(PurpleXfer *xfer)
652: {
653: PeerConnection *conn;
654: size_t size;
655:
656: conn = xfer->data;
657: conn->flags |= PEER_CONNECTION_FLAG_APPROVED;
658:
659: /* Make sure the file size can be represented in 32 bits */
660: size = purple_xfer_get_size(xfer);
661: if (size > G_MAXUINT32)
662: {
663: gchar *tmp, *size1, *size2;
664: size1 = purple_str_size_to_units(size);
665: size2 = purple_str_size_to_units(G_MAXUINT32);
666: tmp = g_strdup_printf(_("File %s is %s, which is larger than "
667: "the maximum size of %s."),
668: xfer->local_filename, size1, size2);
669: purple_xfer_error(purple_xfer_get_type(xfer),
670: purple_xfer_get_account(xfer), xfer->who, tmp);
671: g_free(size1);
672: g_free(size2);
673: g_free(tmp);
674: peer_connection_destroy(conn, OSCAR_DISCONNECT_LOCAL_CLOSED, NULL);
675: return;
676: }
677:
678: /* Keep track of file transfer info */
679: conn->xferdata.totfiles = 1;
680: conn->xferdata.filesleft = 1;
681: conn->xferdata.totparts = 1;
682: conn->xferdata.partsleft = 1;
683: conn->xferdata.totsize = size;
684: conn->xferdata.size = size;
685: conn->xferdata.checksum = 0xffff0000;
686: conn->xferdata.rfrcsum = 0xffff0000;
687: conn->xferdata.rfcsum = 0xffff0000;
688: conn->xferdata.recvcsum = 0xffff0000;
689: strncpy((gchar *)conn->xferdata.idstring, "Cool FileXfer", 31);
690: conn->xferdata.modtime = 0;
691: conn->xferdata.cretime = 0;
692: xfer->filename = g_path_get_basename(xfer->local_filename);
693: conn->xferdata.name_length = MAX(64, strlen(xfer->filename) + 1);
694: conn->xferdata.name = (guchar *)g_strndup(xfer->filename, conn->xferdata.name_length - 1);
695:
696: peer_oft_checksum_file(conn, xfer,
697: peer_oft_checksum_calculated_cb, G_MAXUINT32);
698: }
699:
700: /*
701: * AIM file transfers aren't really meant to be thought
702: * of as a transferring just a single file. The rendezvous
703: * establishes a connection between two computers, and then
704: * those computers can use the same connection for transferring
705: * multiple files. So we don't want the Purple core up and closing
706: * the socket all willy-nilly. We want to do that in the oscar
707: * prpl, whenever one side or the other says they're finished
708: * using the connection. There might be a better way to intercept
709: * the socket from the core...
710: */
711: void
712: peer_oft_sendcb_ack(PurpleXfer *xfer, const guchar *buffer, size_t size)
713: {
714: PeerConnection *conn;
715:
716: conn = xfer->data;
717:
718: /*
719: * If we're done sending, intercept the socket from the core ft code
720: * and wait for the other guy to send the "done" OFT packet.
721: */
722: if (purple_xfer_get_bytes_remaining(xfer) <= 0)
723: {
724: purple_input_remove(xfer->watcher);
725: conn->fd = xfer->fd;
726: xfer->fd = -1;
727: conn->watcher_incoming = purple_input_add(conn->fd,
728: PURPLE_INPUT_READ, peer_connection_recv_cb, conn);
729: }
730: }
731:
732: /*******************************************************************/
733: /* End PurpleXfer callbacks for use when sending a file */
734: /*******************************************************************/
735:
736: /*******************************************************************/
737: /* Begin PurpleXfer callbacks for use when sending and receiving */
738: /*******************************************************************/
739:
740: void
741: peer_oft_cb_generic_cancel(PurpleXfer *xfer)
742: {
743: PeerConnection *conn;
744:
745: conn = xfer->data;
746:
747: if (conn == NULL)
748: return;
749:
750: peer_connection_destroy(conn, OSCAR_DISCONNECT_LOCAL_CLOSED, NULL);
751: }
752:
753: /*******************************************************************/
754: /* End PurpleXfer callbacks for use when sending and receiving */
755: /*******************************************************************/
756:
757: #ifdef TODOFT
758: /*
759: * This little area in oscar.c is the nexus of file transfer code,
760: * so I wrote a little explanation of what happens. I am such a
761: * ninja.
762: *
763: * The series of events for a file send is:
764: * -Create xfer and call purple_xfer_request (this happens in oscar_ask_sendfile)
765: * -User chooses a file and oscar_xfer_init is called. It establishes a
766: * listening socket, then asks the remote user to connect to us (and
767: * gives them the file name, port, IP, etc.)
768: * -They connect to us and we send them an PEER_TYPE_PROMPT (this happens
769: * in peer_oft_recv_frame_established)
770: * -They send us an PEER_TYPE_ACK and then we start sending data
771: * -When we finish, they send us an PEER_TYPE_DONE and they close the
772: * connection.
773: * -We get drunk because file transfer kicks ass.
774: *
775: * The series of events for a file receive is:
776: * -Create xfer and call purple_xfer request (this happens in incomingim_chan2)
777: * -Purple user selects file to name and location to save file to and
778: * oscar_xfer_init is called
779: * -It connects to the remote user using the IP they gave us earlier
780: * -After connecting, they send us an PEER_TYPE_PROMPT. In reply, we send
781: * them an PEER_TYPE_ACK.
782: * -They begin to send us lots of raw data.
783: * -When they finish sending data we send an PEER_TYPE_DONE and then close
784: * the connection.
785: *
786: * Update August 2005:
787: * The series of events for transfers has been seriously complicated by the addition
788: * of transfer redirects and proxied connections. I could throw a whole lot of words
789: * at trying to explain things here, but it probably wouldn't do much good. To get
790: * a better idea of what happens, take a look at the diagrams and documentation
791: * from my Summer of Code project. -- Jonathan Clark
792: */
793:
794: /**
795: * Convert the directory separator from / (0x2f) to ^A (0x01)
796: *
797: * @param name The filename to convert.
798: */
799: static void
800: peer_oft_dirconvert_tostupid(char *name)
801: {
802: while (name[0]) {
803: if (name[0] == 0x01)
804: name[0] = G_DIR_SEPARATOR;
805: name++;
806: }
807: }
808:
809: /**
810: * Convert the directory separator from ^A (0x01) to / (0x2f)
811: *
812: * @param name The filename to convert.
813: */
814: static void
815: peer_oft_dirconvert_fromstupid(char *name)
816: {
817: while (name[0]) {
818: if (name[0] == G_DIR_SEPARATOR)
819: name[0] = 0x01;
820: name++;
821: }
822: }
823: #endif
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>