Mercurial > pidgin.yaz
view libpurple/protocols/mxit/filexfer.c @ 32086:28cab6c40102
propagate from branch 'im.pidgin.pidgin' (head fdb174f23d34033c1e5b7b9258f93d7b0928ba90)
to branch 'im.pidgin.pidgin.mxit' (head 15f4e610ac107f652c16aef6c2f7c2ecbe739a8c)
author | andrew.victor@mxit.com |
---|---|
date | Mon, 09 May 2011 09:49:06 +0000 |
parents | 259bbfb423d4 |
children | 4b15c5c68aa4 |
line wrap: on
line source
/* * MXit Protocol libPurple Plugin * * -- file transfers (sending and receiving) -- * * Pieter Loubser <libpurple@mxit.com> * * (C) Copyright 2009 MXit Lifestyle (Pty) Ltd. * <http://www.mxitlifestyle.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ #include "internal.h" #include "purple.h" #include "protocol.h" #include "mxit.h" #include "chunk.h" #include "filexfer.h" #define MIME_TYPE_OCTETSTREAM "application/octet-stream" /* supported file mime types */ static struct mime_type { const char* magic; const short magic_len; const char* mime; } const mime_types[] = { /* magic length mime */ /* images */ { "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A", 8, "image/png" }, /* image png */ { "\xFF\xD8", 2, "image/jpeg" }, /* image jpeg */ { "\x3C\x3F\x78\x6D\x6C", 5, "image/svg+xml" }, /* image SVGansi */ { "\xEF\xBB\xBF", 3, "image/svg+xml" }, /* image SVGutf */ { "\xEF\xBB\xBF", 3, "image/svg+xml" }, /* image SVGZ */ /* mxit */ { "\x4d\x58\x4d", 3, "application/mxit-msgs" }, /* mxit message */ { "\x4d\x58\x44\x01", 4, "application/mxit-mood" }, /* mxit mood */ { "\x4d\x58\x45\x01", 4, "application/mxit-emo" }, /* mxit emoticon */ { "\x4d\x58\x46\x01", 4, "application/mxit-emof" }, /* mxit emoticon frame */ { "\x4d\x58\x53\x01", 4, "application/mxit-skin" }, /* mxit skin */ /* audio */ { "\x4d\x54\x68\x64", 4, "audio/midi" }, /* audio midi */ { "\x52\x49\x46\x46", 4, "audio/wav" }, /* audio wav */ { "\xFF\xF1", 2, "audio/aac" }, /* audio aac1 */ { "\xFF\xF9", 2, "audio/aac" }, /* audio aac2 */ { "\xFF", 1, "audio/mp3" }, /* audio mp3 */ { "\x23\x21\x41\x4D\x52\x0A", 6, "audio/amr" }, /* audio AMR */ { "\x23\x21\x41\x4D\x52\x2D\x57\x42", 8, "audio/amr-wb" }, /* audio AMR WB */ { "\x00\x00\x00", 3, "audio/mp4" }, /* audio mp4 */ { "\x2E\x73\x6E\x64", 4, "audio/au" } /* audio AU */ }; /*------------------------------------------------------------------------ * Return the MIME type matching the data file. * * @param filename The name of file * @param buf The data * @param buflen The length of the data * @return A MIME type string */ const char* file_mime_type( const char* filename, const char* buf, int buflen ) { unsigned int i; /* check for matching magic headers */ for ( i = 0; i < ARRAY_SIZE( mime_types ); i++ ) { if ( buflen < mime_types[i].magic_len ) /* data is shorter than size of magic */ continue; if ( memcmp( buf, mime_types[i].magic, mime_types[i].magic_len ) == 0 ) return mime_types[i].mime; } /* we did not find the MIME type, so return the default (application/octet-stream) */ return MIME_TYPE_OCTETSTREAM; } /*------------------------------------------------------------------------ * Cleanup and deallocate a MXit file transfer object * * @param xfer The file transfer object */ static void mxit_xfer_free( PurpleXfer* xfer ) { struct mxitxfer* mx = (struct mxitxfer*) xfer->data;; if ( mx ) { g_free( mx ); xfer->data = NULL; } } /*======================================================================================================================== * File Transfer callbacks */ /*------------------------------------------------------------------------ * Initialise a new file transfer. * * @param xfer The file transfer object */ static void mxit_xfer_init( PurpleXfer* xfer ) { struct mxitxfer* mx = (struct mxitxfer*) xfer->data; purple_debug_info( MXIT_PLUGIN_ID, "mxit_xfer_init\n" ); if ( purple_xfer_get_type( xfer ) == PURPLE_XFER_SEND ) { /* we are trying to send a file to MXit */ if ( purple_xfer_get_size( xfer ) > CP_MAX_FILESIZE ) { /* the file is too big */ purple_xfer_error( xfer->type, xfer->account, xfer->who, _( "The file you are trying to send is too large!" ) ); purple_xfer_cancel_local( xfer ); return; } /* start the file transfer */ purple_xfer_start( xfer, -1, NULL, 0 ); } else { /* * we have just accepted a file transfer request from MXit. send a confirmation * to the MXit server so that can send us the file */ mxit_send_file_accept( mx->session, mx->fileid, purple_xfer_get_size( xfer ), 0 ); } } /*------------------------------------------------------------------------ * Start the file transfer. * * @param xfer The file transfer object */ static void mxit_xfer_start( PurpleXfer* xfer ) { unsigned char* buffer; int size; int wrote; purple_debug_info( MXIT_PLUGIN_ID, "mxit_xfer_start\n" ); if ( purple_xfer_get_type( xfer ) == PURPLE_XFER_SEND ) { /* * the user wants to send a file to one of his contacts. we need to create * a buffer and copy the file data into memory and then we can send it to * the contact. we will send the whole file with one go. */ buffer = g_malloc( xfer->bytes_remaining ); size = fread( buffer, xfer->bytes_remaining, 1, xfer->dest_fp ); wrote = purple_xfer_write( xfer, buffer, xfer->bytes_remaining ); if ( wrote > 0 ) purple_xfer_set_bytes_sent( xfer, wrote ); /* free the buffer */ g_free( buffer ); buffer = NULL; } } /*------------------------------------------------------------------------ * The file transfer has ended. * * @param xfer The file transfer object */ static void mxit_xfer_end( PurpleXfer* xfer ) { purple_debug_info( MXIT_PLUGIN_ID, "mxit_xfer_end\n" ); /* deallocate object */ mxit_xfer_free( xfer ); } /*------------------------------------------------------------------------ * The file transfer (to a user) has been cancelled. * * @param xfer The file transfer object */ static void mxit_xfer_cancel_send( PurpleXfer* xfer ) { purple_debug_info( MXIT_PLUGIN_ID, "mxit_xfer_cancel_send\n" ); /* deallocate object */ mxit_xfer_free( xfer ); } /*------------------------------------------------------------------------ * Send the file data. * * @param buffer The data to sent * @param size The length of the data to send * @param xfer The file transfer object * @return The amount of data actually sent */ static gssize mxit_xfer_write( const guchar* buffer, size_t size, PurpleXfer* xfer ) { struct mxitxfer* mx = (struct mxitxfer*) xfer->data; purple_debug_info( MXIT_PLUGIN_ID, "mxit_xfer_write\n" ); if ( !mx ) { purple_debug_warning( MXIT_PLUGIN_ID, "mxit_xfer_write: invalid internal mxit xfer data\n" ); return -1; } else if ( purple_xfer_get_type( xfer ) != PURPLE_XFER_SEND ) { purple_debug_warning( MXIT_PLUGIN_ID, "mxit_xfer_write: wrong xfer type received\n" ); return -1; } /* create and send the packet to MXit */ mxit_send_file( mx->session, purple_xfer_get_remote_user( xfer ), purple_xfer_get_filename( xfer ), buffer, size ); /* the transfer is complete */ purple_xfer_set_completed( xfer, TRUE ); return size; } /*------------------------------------------------------------------------ * The user has rejected a file offer from MXit. * * @param xfer The file transfer object */ static void mxit_xfer_request_denied( PurpleXfer* xfer ) { struct mxitxfer* mx = (struct mxitxfer*) xfer->data; purple_debug_info( MXIT_PLUGIN_ID, "mxit_xfer_request_denied\n" ); /* send file reject packet to MXit server */ mxit_send_file_reject( mx->session, mx->fileid ); /* deallocate object */ mxit_xfer_free( xfer ); } /*------------------------------------------------------------------------ * The file transfer (from MXit) has been cancelled. */ static void mxit_xfer_cancel_recv( PurpleXfer* xfer ) { purple_debug_info( MXIT_PLUGIN_ID, "mxit_xfer_cancel_recv\n" ); /* deallocate object */ mxit_xfer_free( xfer ); } /*======================================================================================================================== * Callbacks from libPurple */ /*------------------------------------------------------------------------ * Indicate if file transfers are supported to this contact. * For MXit file transfers are always supported. * * @param gc The connection object * @param who The username of the contact * @return TRUE if file transfers are supported */ gboolean mxit_xfer_enabled( PurpleConnection* gc, const char* who ) { return TRUE; } /*------------------------------------------------------------------------ * Create and initialize a new file transfer to a contact. * * @param gc The connection object * @param who The username of the recipient */ PurpleXfer* mxit_xfer_new( PurpleConnection* gc, const char* who ) { struct MXitSession* session = (struct MXitSession*) gc->proto_data; PurpleXfer* xfer = NULL; struct mxitxfer* mx = NULL; /* (reference: "libpurple/ft.h") */ xfer = purple_xfer_new( session->acc, PURPLE_XFER_SEND, who ); /* create file info and attach it to the file transfer */ mx = g_new0( struct mxitxfer, 1 ); mx->session = session; xfer->data = mx; /* configure callbacks (reference: "libpurple/ft.h") */ purple_xfer_set_init_fnc( xfer, mxit_xfer_init ); purple_xfer_set_start_fnc( xfer, mxit_xfer_start ); purple_xfer_set_end_fnc( xfer, mxit_xfer_end ); purple_xfer_set_cancel_send_fnc( xfer, mxit_xfer_cancel_send ); purple_xfer_set_write_fnc( xfer, mxit_xfer_write ); return xfer; } /*------------------------------------------------------------------------ * The user has initiated a file transfer to a contact. * * @param gc The connection object * @param who The username of the contact * @param filename The filename (is NULL if request has not been accepted yet) */ void mxit_xfer_tx( PurpleConnection* gc, const char* who, const char* filename ) { PurpleXfer *xfer = mxit_xfer_new( gc, who ); if ( filename ) purple_xfer_request_accepted( xfer, filename ); else purple_xfer_request( xfer ); } /*======================================================================================================================== * Calls from the MXit Protocol layer */ /*------------------------------------------------------------------------ * A file transfer offer has been received from the MXit server. * * @param session The MXit session object * @param usermame The username of the sender * @param filename The name of the file being offered * @param filesize The size of the file being offered * @param fileid A unique ID that identifies this file */ void mxit_xfer_rx_offer( struct MXitSession* session, const char* username, const char* filename, int filesize, const char* fileid ) { PurpleXfer* xfer = NULL; struct mxitxfer* mx = NULL; purple_debug_info( MXIT_PLUGIN_ID, "File Offer: file=%s, from=%s, size=%i\n", filename, username, filesize ); xfer = purple_xfer_new( session->acc, PURPLE_XFER_RECEIVE, username ); if ( xfer ) { /* create a new mxit xfer struct for internal use */ mx = g_new0( struct mxitxfer, 1 ); mx->session = session; memcpy( mx->fileid, fileid, MXIT_CHUNK_FILEID_LEN ); xfer->data = mx; purple_xfer_set_filename( xfer, filename ); if( filesize > 0 ) purple_xfer_set_size( xfer, filesize ); /* register file transfer callback functions */ purple_xfer_set_init_fnc( xfer, mxit_xfer_init ); purple_xfer_set_request_denied_fnc( xfer, mxit_xfer_request_denied ); purple_xfer_set_cancel_recv_fnc( xfer, mxit_xfer_cancel_recv ); purple_xfer_set_end_fnc( xfer, mxit_xfer_end ); /* give the request to the user to accept/deny */ purple_xfer_request( xfer ); } } /*------------------------------------------------------------------------ * Return the libPurple file-transfer object associated with a MXit transfer * * @param session The MXit session object * @param fileid A unique ID that identifies this file */ static PurpleXfer* find_mxit_xfer( struct MXitSession* session, const char* fileid ) { GList* item = NULL; PurpleXfer* xfer = NULL; item = purple_xfers_get_all(); /* list of all active transfers */ while ( item ) { xfer = item->data; if ( xfer->account == session->acc ) { /* transfer is associated with this MXit account */ struct mxitxfer* mx = xfer->data; /* does the fileid match? */ if ( ( mx ) && ( memcmp( mx->fileid, fileid, MXIT_CHUNK_FILEID_LEN ) == 0 ) ) break; } item = g_list_next( item ); } if ( item ) return item->data; else return NULL; } /*------------------------------------------------------------------------ * A file has been received from the MXit server. * * @param session The MXit session object * @param fileid A unique ID that identifies this file * @param data The file data * @param datalen The size of the data */ void mxit_xfer_rx_file( struct MXitSession* session, const char* fileid, const char* data, int datalen ) { PurpleXfer* xfer = NULL; struct mxitxfer* mx = NULL; purple_debug_info( MXIT_PLUGIN_ID, "mxit_xfer_rx_file: (size=%i)\n", datalen ); /* find the file-transfer object */ xfer = find_mxit_xfer( session, fileid ); if ( xfer ) { mx = xfer->data; /* this is the transfer we have been looking for */ purple_xfer_ref( xfer ); purple_xfer_start( xfer, -1, NULL, 0 ); fwrite( data, datalen, 1, xfer->dest_fp ); purple_xfer_unref( xfer ); purple_xfer_set_completed( xfer, TRUE ); purple_xfer_end( xfer ); /* inform MXit that file was successfully received */ mxit_send_file_received( session, fileid, RECV_STATUS_SUCCESS ); } else { /* file transfer not found */ mxit_send_file_received( session, fileid, RECV_STATUS_BAD_ID ); } }