File:  [Coherent Logic Development] / ChivanetAimPidgin / oscarprpl / src / c / oft.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Mon Jan 27 19:48:25 2025 UTC (6 months ago) by snw
Branches: MAIN, CoherentLogicDevelopment
CVS tags: test-tag, start, HEAD
Pidgin AIM Plugin for ChivaNet

    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>