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>