Mercurial > pidgin.yaz
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 } |