comparison libpurple/protocols/mxit/filexfer.c @ 28903:69aa4660401a

Initial addition of the MXit protocol plugin, provided by the MXit folks themselves.
author John Bailey <rekkanoryo@rekkanoryo.org>
date Sun, 08 Nov 2009 23:55:56 +0000
parents
children 259bbfb423d4
comparison
equal deleted inserted replaced
28901:13e668ef158d 28903:69aa4660401a
1 /*
2 * MXit Protocol libPurple Plugin
3 *
4 * -- file transfers (sending and receiving) --
5 *
6 * Pieter Loubser <libpurple@mxit.com>
7 *
8 * (C) Copyright 2009 MXit Lifestyle (Pty) Ltd.
9 * <http://www.mxitlifestyle.com>
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
24 */
25
26 #include <stdio.h>
27 #include <unistd.h>
28 #include <string.h>
29
30 #include "purple.h"
31 #include "protocol.h"
32 #include "mxit.h"
33 #include "chunk.h"
34 #include "filexfer.h"
35
36
37 #define MIME_TYPE_OCTETSTREAM "application/octet-stream"
38
39
40 /* supported file mime types */
41 static struct mime_type {
42 const char* magic;
43 const short magic_len;
44 const char* mime;
45 } const mime_types[] = {
46 /* magic length mime */
47 /* images */ { "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A", 8, "image/png" }, /* image png */
48 { "\xFF\xD8", 2, "image/jpeg" }, /* image jpeg */
49 { "\x3C\x3F\x78\x6D\x6C", 5, "image/svg+xml" }, /* image SVGansi */
50 { "\xEF\xBB\xBF", 3, "image/svg+xml" }, /* image SVGutf */
51 { "\xEF\xBB\xBF", 3, "image/svg+xml" }, /* image SVGZ */
52 /* mxit */ { "\x4d\x58\x4d", 3, "application/mxit-msgs" }, /* mxit message */
53 { "\x4d\x58\x44\x01", 4, "application/mxit-mood" }, /* mxit mood */
54 { "\x4d\x58\x45\x01", 4, "application/mxit-emo" }, /* mxit emoticon */
55 { "\x4d\x58\x46\x01", 4, "application/mxit-emof" }, /* mxit emoticon frame */
56 { "\x4d\x58\x53\x01", 4, "application/mxit-skin" }, /* mxit skin */
57 /* audio */ { "\x4d\x54\x68\x64", 4, "audio/midi" }, /* audio midi */
58 { "\x52\x49\x46\x46", 4, "audio/wav" }, /* audio wav */
59 { "\xFF\xF1", 2, "audio/aac" }, /* audio aac1 */
60 { "\xFF\xF9", 2, "audio/aac" }, /* audio aac2 */
61 { "\xFF", 1, "audio/mp3" }, /* audio mp3 */
62 { "\x23\x21\x41\x4D\x52\x0A", 6, "audio/amr" }, /* audio AMR */
63 { "\x23\x21\x41\x4D\x52\x2D\x57\x42", 8, "audio/amr-wb" }, /* audio AMR WB */
64 { "\x00\x00\x00", 3, "audio/mp4" }, /* audio mp4 */
65 { "\x2E\x73\x6E\x64", 4, "audio/au" } /* audio AU */
66 };
67
68
69 /*------------------------------------------------------------------------
70 * Return the MIME type matching the data file.
71 *
72 * @param filename The name of file
73 * @param buf The data
74 * @param buflen The length of the data
75 * @return A MIME type string
76 */
77 const char* file_mime_type( const char* filename, const char* buf, int buflen )
78 {
79 unsigned int i;
80
81 /* check for matching magic headers */
82 for ( i = 0; i < ARRAY_SIZE( mime_types ); i++ ) {
83
84 if ( buflen < mime_types[i].magic_len ) /* data is shorter than size of magic */
85 continue;
86
87 if ( memcmp( buf, mime_types[i].magic, mime_types[i].magic_len ) == 0 )
88 return mime_types[i].mime;
89 }
90
91 /* we did not find the MIME type, so return the default (application/octet-stream) */
92 return MIME_TYPE_OCTETSTREAM;
93 }
94
95
96 /*------------------------------------------------------------------------
97 * Cleanup and deallocate a MXit file transfer object
98 *
99 * @param xfer The file transfer object
100 */
101 static void mxit_xfer_free( PurpleXfer* xfer )
102 {
103 struct mxitxfer* mx = (struct mxitxfer*) xfer->data;;
104
105 if ( mx ) {
106 g_free( mx );
107 xfer->data = NULL;
108 }
109 }
110
111
112 /*========================================================================================================================
113 * File Transfer callbacks
114 */
115
116 /*------------------------------------------------------------------------
117 * Initialise a new file transfer.
118 *
119 * @param xfer The file transfer object
120 */
121 static void mxit_xfer_init( PurpleXfer* xfer )
122 {
123 struct mxitxfer* mx = (struct mxitxfer*) xfer->data;
124
125 purple_debug_info( MXIT_PLUGIN_ID, "mxit_xfer_init\n" );
126
127 if ( purple_xfer_get_type( xfer ) == PURPLE_XFER_SEND ) {
128 /* we are trying to send a file to MXit */
129
130 if ( purple_xfer_get_size( xfer ) > CP_MAX_FILESIZE ) {
131 /* the file is too big */
132 purple_xfer_error( xfer->type, xfer->account, xfer->who, _( "The file you are trying to send is too large!" ) );
133 purple_xfer_cancel_local( xfer );
134 return;
135 }
136
137 /* start the file transfer */
138 purple_xfer_start( xfer, -1, NULL, 0 );
139 }
140 else {
141 /*
142 * we have just accepted a file transfer request from MXit. send a confirmation
143 * to the MXit server so that can send us the file
144 */
145 mxit_send_file_accept( mx->session, mx->fileid, purple_xfer_get_size( xfer ), 0 );
146 }
147 }
148
149
150 /*------------------------------------------------------------------------
151 * Start the file transfer.
152 *
153 * @param xfer The file transfer object
154 */
155 static void mxit_xfer_start( PurpleXfer* xfer )
156 {
157 unsigned char* buffer;
158 int size;
159 int wrote;
160
161 purple_debug_info( MXIT_PLUGIN_ID, "mxit_xfer_start\n" );
162
163 if ( purple_xfer_get_type( xfer ) == PURPLE_XFER_SEND ) {
164 /*
165 * the user wants to send a file to one of his contacts. we need to create
166 * a buffer and copy the file data into memory and then we can send it to
167 * the contact. we will send the whole file with one go.
168 */
169 buffer = g_malloc( xfer->bytes_remaining );
170 size = fread( buffer, xfer->bytes_remaining, 1, xfer->dest_fp );
171
172 wrote = purple_xfer_write( xfer, buffer, xfer->bytes_remaining );
173 if ( wrote > 0 )
174 purple_xfer_set_bytes_sent( xfer, wrote );
175
176 /* free the buffer */
177 g_free( buffer );
178 buffer = NULL;
179 }
180 }
181
182
183 /*------------------------------------------------------------------------
184 * The file transfer has ended.
185 *
186 * @param xfer The file transfer object
187 */
188 static void mxit_xfer_end( PurpleXfer* xfer )
189 {
190 purple_debug_info( MXIT_PLUGIN_ID, "mxit_xfer_end\n" );
191
192 /* deallocate object */
193 mxit_xfer_free( xfer );
194 }
195
196
197 /*------------------------------------------------------------------------
198 * The file transfer (to a user) has been cancelled.
199 *
200 * @param xfer The file transfer object
201 */
202 static void mxit_xfer_cancel_send( PurpleXfer* xfer )
203 {
204 purple_debug_info( MXIT_PLUGIN_ID, "mxit_xfer_cancel_send\n" );
205
206 /* deallocate object */
207 mxit_xfer_free( xfer );
208 }
209
210
211 /*------------------------------------------------------------------------
212 * Send the file data.
213 *
214 * @param buffer The data to sent
215 * @param size The length of the data to send
216 * @param xfer The file transfer object
217 * @return The amount of data actually sent
218 */
219 static gssize mxit_xfer_write( const guchar* buffer, size_t size, PurpleXfer* xfer )
220 {
221 struct mxitxfer* mx = (struct mxitxfer*) xfer->data;
222
223 purple_debug_info( MXIT_PLUGIN_ID, "mxit_xfer_write\n" );
224
225 if ( !mx ) {
226 purple_debug_warning( MXIT_PLUGIN_ID, "mxit_xfer_write: invalid internal mxit xfer data\n" );
227 return -1;
228 }
229 else if ( purple_xfer_get_type( xfer ) != PURPLE_XFER_SEND ) {
230 purple_debug_warning( MXIT_PLUGIN_ID, "mxit_xfer_write: wrong xfer type received\n" );
231 return -1;
232 }
233
234 /* create and send the packet to MXit */
235 mxit_send_file( mx->session, purple_xfer_get_remote_user( xfer ), purple_xfer_get_filename( xfer ), buffer, size );
236
237 /* the transfer is complete */
238 purple_xfer_set_completed( xfer, TRUE );
239
240 return size;
241 }
242
243
244 /*------------------------------------------------------------------------
245 * The user has rejected a file offer from MXit.
246 *
247 * @param xfer The file transfer object
248 */
249 static void mxit_xfer_request_denied( PurpleXfer* xfer )
250 {
251 struct mxitxfer* mx = (struct mxitxfer*) xfer->data;
252
253 purple_debug_info( MXIT_PLUGIN_ID, "mxit_xfer_request_denied\n" );
254
255 /* send file reject packet to MXit server */
256 mxit_send_file_reject( mx->session, mx->fileid );
257
258 /* deallocate object */
259 mxit_xfer_free( xfer );
260 }
261
262
263 /*------------------------------------------------------------------------
264 * The file transfer (from MXit) has been cancelled.
265 */
266 static void mxit_xfer_cancel_recv( PurpleXfer* xfer )
267 {
268 purple_debug_info( MXIT_PLUGIN_ID, "mxit_xfer_cancel_recv\n" );
269
270 /* deallocate object */
271 mxit_xfer_free( xfer );
272 }
273
274
275 /*========================================================================================================================
276 * Callbacks from libPurple
277 */
278
279 /*------------------------------------------------------------------------
280 * Indicate if file transfers are supported to this contact.
281 * For MXit file transfers are always supported.
282 *
283 * @param gc The connection object
284 * @param who The username of the contact
285 * @return TRUE if file transfers are supported
286 */
287 gboolean mxit_xfer_enabled( PurpleConnection* gc, const char* who )
288 {
289 return TRUE;
290 }
291
292
293 /*------------------------------------------------------------------------
294 * Create and initialize a new file transfer to a contact.
295 *
296 * @param gc The connection object
297 * @param who The username of the recipient
298 */
299 PurpleXfer* mxit_xfer_new( PurpleConnection* gc, const char* who )
300 {
301 struct MXitSession* session = (struct MXitSession*) gc->proto_data;
302 PurpleXfer* xfer = NULL;
303 struct mxitxfer* mx = NULL;
304
305 /* (reference: "libpurple/ft.h") */
306 xfer = purple_xfer_new( session->acc, PURPLE_XFER_SEND, who );
307
308 /* create file info and attach it to the file transfer */
309 mx = g_new0( struct mxitxfer, 1 );
310 mx->session = session;
311 xfer->data = mx;
312
313 /* configure callbacks (reference: "libpurple/ft.h") */
314 purple_xfer_set_init_fnc( xfer, mxit_xfer_init );
315 purple_xfer_set_start_fnc( xfer, mxit_xfer_start );
316 purple_xfer_set_end_fnc( xfer, mxit_xfer_end );
317 purple_xfer_set_cancel_send_fnc( xfer, mxit_xfer_cancel_send );
318 purple_xfer_set_write_fnc( xfer, mxit_xfer_write );
319
320 return xfer;
321 }
322
323
324 /*------------------------------------------------------------------------
325 * The user has initiated a file transfer to a contact.
326 *
327 * @param gc The connection object
328 * @param who The username of the contact
329 * @param filename The filename (is NULL if request has not been accepted yet)
330 */
331 void mxit_xfer_tx( PurpleConnection* gc, const char* who, const char* filename )
332 {
333 PurpleXfer *xfer = mxit_xfer_new( gc, who );
334
335 if ( filename )
336 purple_xfer_request_accepted( xfer, filename );
337 else
338 purple_xfer_request( xfer );
339 }
340
341
342 /*========================================================================================================================
343 * Calls from the MXit Protocol layer
344 */
345
346 /*------------------------------------------------------------------------
347 * A file transfer offer has been received from the MXit server.
348 *
349 * @param session The MXit session object
350 * @param usermame The username of the sender
351 * @param filename The name of the file being offered
352 * @param filesize The size of the file being offered
353 * @param fileid A unique ID that identifies this file
354 */
355 void mxit_xfer_rx_offer( struct MXitSession* session, const char* username, const char* filename, int filesize, const char* fileid )
356 {
357 PurpleXfer* xfer = NULL;
358 struct mxitxfer* mx = NULL;
359
360 purple_debug_info( MXIT_PLUGIN_ID, "File Offer: file=%s, from=%s, size=%i\n", filename, username, filesize );
361
362 xfer = purple_xfer_new( session->acc, PURPLE_XFER_RECEIVE, username );
363 if ( xfer ) {
364 /* create a new mxit xfer struct for internal use */
365 mx = g_new0( struct mxitxfer, 1 );
366 mx->session = session;
367 memcpy( mx->fileid, fileid, MXIT_CHUNK_FILEID_LEN );
368 xfer->data = mx;
369
370 purple_xfer_set_filename( xfer, filename );
371 if( filesize > 0 )
372 purple_xfer_set_size( xfer, filesize );
373
374 /* register file transfer callback functions */
375 purple_xfer_set_init_fnc( xfer, mxit_xfer_init );
376 purple_xfer_set_request_denied_fnc( xfer, mxit_xfer_request_denied );
377 purple_xfer_set_cancel_recv_fnc( xfer, mxit_xfer_cancel_recv );
378 purple_xfer_set_end_fnc( xfer, mxit_xfer_end );
379
380 /* give the request to the user to accept/deny */
381 purple_xfer_request( xfer );
382 }
383 }
384
385
386 /*------------------------------------------------------------------------
387 * Return the libPurple file-transfer object associated with a MXit transfer
388 *
389 * @param session The MXit session object
390 * @param fileid A unique ID that identifies this file
391 */
392 static PurpleXfer* find_mxit_xfer( struct MXitSession* session, const char* fileid )
393 {
394 GList* item = NULL;
395 PurpleXfer* xfer = NULL;
396
397 item = purple_xfers_get_all(); /* list of all active transfers */
398 while ( item ) {
399 xfer = item->data;
400
401 if ( xfer->account == session->acc ) {
402 /* transfer is associated with this MXit account */
403 struct mxitxfer* mx = xfer->data;
404
405 /* does the fileid match? */
406 if ( ( mx ) && ( memcmp( mx->fileid, fileid, MXIT_CHUNK_FILEID_LEN ) == 0 ) )
407 break;
408 }
409
410 item = g_list_next( item );
411 }
412
413 if ( item )
414 return item->data;
415 else
416 return NULL;
417 }
418
419 /*------------------------------------------------------------------------
420 * A file has been received from the MXit server.
421 *
422 * @param session The MXit session object
423 * @param fileid A unique ID that identifies this file
424 * @param data The file data
425 * @param datalen The size of the data
426 */
427 void mxit_xfer_rx_file( struct MXitSession* session, const char* fileid, const char* data, int datalen )
428 {
429 PurpleXfer* xfer = NULL;
430 struct mxitxfer* mx = NULL;
431
432 purple_debug_info( MXIT_PLUGIN_ID, "mxit_xfer_rx_file: (size=%i)\n", datalen );
433
434 /* find the file-transfer object */
435 xfer = find_mxit_xfer( session, fileid );
436 if ( xfer ) {
437 mx = xfer->data;
438
439 /* this is the transfer we have been looking for */
440 purple_xfer_ref( xfer );
441 purple_xfer_start( xfer, -1, NULL, 0 );
442 fwrite( data, datalen, 1, xfer->dest_fp );
443 purple_xfer_unref( xfer );
444 purple_xfer_set_completed( xfer, TRUE );
445 purple_xfer_end( xfer );
446
447 /* inform MXit that file was successfully received */
448 mxit_send_file_received( session, fileid, RECV_STATUS_SUCCESS );
449 }
450 else {
451 /* file transfer not found */
452 mxit_send_file_received( session, fileid, RECV_STATUS_BAD_ID );
453 }
454 }