source: protocols/purple/ft.c @ e7dc02a

Last change on this file since e7dc02a was e7dc02a, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-05-19T00:57:58Z

Similar hacky code to send files. This indirect sending stuff sucks badly
for numerous reasons. Maybe libpurple 2.7.0 is less crappy and will
eventually allow (working) direct ft's again.

This somewhat works, but filename info is lost with some protocols.

  • Property mode set to 100644
File size: 9.0 KB
Line 
1/***************************************************************************\
2*                                                                           *
3*  BitlBee - An IRC to IM gateway                                           *
4*  libpurple module - File transfer stuff                                   *
5*                                                                           *
6*  Copyright 2009-2010 Wilmer van der Gaast <wilmer@gaast.net>              *
7*                                                                           *
8*  This program is free software; you can redistribute it and/or modify     *
9*  it under the terms of the GNU General Public License as published by     *
10*  the Free Software Foundation; either version 2 of the License, or        *
11*  (at your option) any later version.                                      *
12*                                                                           *
13*  This program is distributed in the hope that it will be useful,          *
14*  but WITHOUT ANY WARRANTY; without even the implied warranty of           *
15*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            *
16*  GNU General Public License for more details.                             *
17*                                                                           *
18*  You should have received a copy of the GNU General Public License along  *
19*  with this program; if not, write to the Free Software Foundation, Inc.,  *
20*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              *
21*                                                                           *
22\***************************************************************************/
23
24/* Do file transfers via disk for now, since libpurple was really designed
25   for straight-to/from disk fts and is only just learning how to pass the
26   file contents the the UI instead (2.6.0 and higher it seems, and with
27   varying levels of success). */
28
29#include "bitlbee.h"
30
31#include <stdarg.h>
32
33#include <glib.h>
34#include <purple.h>
35
36struct prpl_xfer_data
37{
38        PurpleXfer *xfer;
39        file_transfer_t *ft;
40        struct im_connection *ic;
41        int fd;
42        char *fn, *orig_fn, *handle;
43        gboolean ui_wants_data;
44};
45
46static file_transfer_t *next_ft;
47
48struct im_connection *purple_ic_by_pa( PurpleAccount *pa );
49static gboolean prplcb_xfer_new_send_cb( gpointer data, gint fd, b_input_condition cond );
50static gboolean prpl_xfer_write_request( struct file_transfer *ft );
51
52
53/* Receiving files (IM->UI): */
54static void prpl_xfer_accept( struct file_transfer *ft )
55{
56        struct prpl_xfer_data *px = ft->data;
57        purple_xfer_request_accepted( px->xfer, NULL );
58        prpl_xfer_write_request( ft );
59}
60
61static void prpl_xfer_canceled( struct file_transfer *ft, char *reason )
62{
63        struct prpl_xfer_data *px = ft->data;
64        purple_xfer_request_denied( px->xfer );
65}
66
67static void prplcb_xfer_new( PurpleXfer *xfer )
68{
69        if( purple_xfer_get_type( xfer ) == PURPLE_XFER_RECEIVE )
70        {
71                struct prpl_xfer_data *px = g_new0( struct prpl_xfer_data, 1 );
72               
73                xfer->ui_data = px;
74                px->xfer = xfer;
75                px->fn = mktemp( g_strdup( "/tmp/bitlbee-purple-ft.XXXXXX" ) );
76                px->fd = -1;
77               
78                purple_xfer_set_local_filename( xfer, px->fn );
79               
80                /* Sadly the xfer struct is still empty ATM so come back after
81                   the caller is done. */
82                b_timeout_add( 0, prplcb_xfer_new_send_cb, xfer );
83        }
84        else
85        {
86                struct file_transfer *ft = next_ft;
87                struct prpl_xfer_data *px = ft->data;
88               
89                xfer->ui_data = px;
90                px->xfer = xfer;
91               
92                purple_xfer_set_filename( xfer, px->orig_fn );
93                purple_xfer_set_local_filename( xfer, px->fn );
94               
95                next_ft = NULL;
96        }
97}
98
99static gboolean prplcb_xfer_new_send_cb( gpointer data, gint fd, b_input_condition cond )
100{
101        PurpleXfer *xfer = data;
102        struct im_connection *ic = purple_ic_by_pa( xfer->account );
103        struct prpl_xfer_data *px = xfer->ui_data;
104        PurpleBuddy *buddy;
105        const char *who;
106       
107        buddy = purple_find_buddy( xfer->account, xfer->who );
108        who = buddy ? purple_buddy_get_name( buddy ) : xfer->who;
109       
110        /* TODO(wilmer): After spreading some more const goodness in BitlBee,
111           remove the evil cast below. */
112        px->ft = imcb_file_send_start( ic, (char*) who, xfer->filename, xfer->size );
113        px->ft->data = px;
114       
115        px->ft->accept = prpl_xfer_accept;
116        px->ft->canceled = prpl_xfer_canceled;
117        px->ft->write_request = prpl_xfer_write_request;
118       
119        return FALSE;
120}
121
122gboolean try_write_to_ui( gpointer data, gint fd, b_input_condition cond )
123{
124        struct file_transfer *ft = data;
125        struct prpl_xfer_data *px = ft->data;
126        struct stat fs;
127        off_t tx_bytes;
128       
129        /* If we don't have the file opened yet, there's no data so wait. */
130        if( px->fd < 0 || !px->ui_wants_data )
131                return FALSE;
132       
133        tx_bytes = lseek( px->fd, 0, SEEK_CUR );
134        fstat( px->fd, &fs );
135       
136        if( fs.st_size > tx_bytes )
137        {
138                char buf[1024];
139                size_t n = MIN( fs.st_size - tx_bytes, sizeof( buf ) );
140               
141                if( read( px->fd, buf, n ) == n && ft->write( ft, buf, n ) )
142                {
143                        px->ui_wants_data = FALSE;
144                }
145                else
146                {
147                        purple_xfer_cancel_local( px->xfer );
148                        imcb_file_canceled( ft, "Read error" );
149                }
150        }
151       
152        if( lseek( px->fd, 0, SEEK_CUR ) == px->xfer->size )
153        {
154                purple_xfer_end( px->xfer );
155                imcb_file_finished( ft );
156        }
157       
158        return FALSE;
159}
160
161/* UI calls this when its buffer is empty and wants more data to send to the user. */
162static gboolean prpl_xfer_write_request( struct file_transfer *ft )
163{
164        struct prpl_xfer_data *px = ft->data;
165       
166        px->ui_wants_data = TRUE;
167        try_write_to_ui( ft, 0, 0 );
168       
169        return FALSE;
170}
171
172
173/* Generic (IM<>UI): */
174static void prplcb_xfer_destroy( PurpleXfer *xfer )
175{
176        struct prpl_xfer_data *px = xfer->ui_data;
177       
178        g_free( px->fn );
179        g_free( px->orig_fn );
180        g_free( px->handle );
181        if( px->fd >= 0 )
182                close( px->fd );
183        g_free( px );
184}
185
186static void prplcb_xfer_progress( PurpleXfer *xfer, double percent )
187{
188        struct prpl_xfer_data *px = xfer->ui_data;
189       
190        if( px == NULL )
191                return;
192       
193        if( purple_xfer_get_type( xfer ) == PURPLE_XFER_SEND )
194        {
195                if( *px->fn )
196                {
197                        //unlink( px->fn );
198                        *px->fn = '\0';
199                }
200               
201                return;
202        }
203       
204        if( px->fd == -1 && percent > 0 )
205        {
206                /* Weeeeeeeee, we're getting data! That means the file exists
207                   by now so open it and start sending to the UI. */
208                px->fd = open( px->fn, O_RDONLY );
209               
210                /* Unlink it now, because we don't need it after this. */
211                unlink( px->fn );
212        }
213       
214        if( percent < 1 )
215                try_write_to_ui( px->ft, 0, 0 );
216        else
217                /* Another nice problem: If we have the whole file, it only
218                   gets closed when we return. Problem: There may still be
219                   stuff buffered and not written, we'll only see it after
220                   the caller close()s the file. So poll the file after that. */
221                b_timeout_add( 0, try_write_to_ui, px->ft );
222}
223
224static void prplcb_xfer_cancel_remote( PurpleXfer *xfer )
225{
226        struct prpl_xfer_data *px = xfer->ui_data;
227       
228        imcb_file_canceled( px->ft, "Canceled by remote end" );
229}
230
231static void prplcb_xfer_add( PurpleXfer *xfer )
232{
233        if( purple_xfer_get_type( xfer ) == PURPLE_XFER_SEND )
234        {
235                struct prpl_xfer_data *px = xfer->ui_data;
236               
237                purple_xfer_set_filename( xfer, px->orig_fn );
238        }
239}
240
241static void prplcb_xfer_dbg( PurpleXfer *xfer )
242{
243        fprintf( stderr, "prplcb_xfer_dbg 0x%p\n", xfer );
244}
245
246
247/* Sending files (UI->IM): */
248static gboolean prpl_xfer_write( struct file_transfer *ft, char *buffer, unsigned int len );
249static gboolean purple_transfer_request_cb( gpointer data, gint fd, b_input_condition cond );
250
251void purple_transfer_request( struct im_connection *ic, file_transfer_t *ft, char *handle )
252{
253        struct prpl_xfer_data *px = g_new0( struct prpl_xfer_data, 1 );
254       
255        ft->data = px;
256        px->ft = ft;
257        px->fn = g_strdup( "/tmp/bitlbee-purple-ft.XXXXXX" );
258        px->fd = mkstemp( px->fn );
259       
260        px->ic = ic;
261        px->handle = g_strdup( handle );
262        px->orig_fn = g_strdup( ft->file_name );
263       
264        imcb_log( ic, "Due to libpurple limitations, the file has to be cached locally before proceeding with the actual file transfer. Please wait..." );
265       
266        b_timeout_add( 0, purple_transfer_request_cb, ft );
267}
268
269static void purple_transfer_forward( struct file_transfer *ft )
270{
271        struct prpl_xfer_data *px = ft->data;
272        PurpleAccount *pa = px->ic->proto_data;
273       
274        /* xfer_new() will pick up this variable. It's a hack but we're not
275           multi-threaded anyway. */
276        next_ft = ft;
277        serv_send_file( purple_account_get_connection( pa ), px->handle, px->fn );
278}
279
280static gboolean purple_transfer_request_cb( gpointer data, gint fd, b_input_condition cond )
281{
282        file_transfer_t *ft = data;
283       
284        if( ft->write == NULL )
285        {
286                ft->write = prpl_xfer_write;
287                imcb_file_recv_start( ft );
288        }
289       
290        ft->write_request( ft );
291       
292        return FALSE;
293}
294
295static gboolean prpl_xfer_write( struct file_transfer *ft, char *buffer, unsigned int len )
296{
297        struct prpl_xfer_data *px = ft->data;
298       
299        if( write( px->fd, buffer, len ) != len )
300        {
301                imcb_file_canceled( ft, "Error while writing temporary file" );
302                return FALSE;
303        }
304       
305        if( lseek( px->fd, 0, SEEK_CUR ) >= ft->file_size )
306        {
307                close( px->fd );
308                px->fd = -1;
309               
310                purple_transfer_forward( ft );
311                imcb_file_finished( ft );
312                px->ft = NULL;
313        }
314        else
315                b_timeout_add( 0, purple_transfer_request_cb, ft );
316       
317        return TRUE;
318}
319
320
321
322PurpleXferUiOps bee_xfer_uiops =
323{
324        prplcb_xfer_new,
325        prplcb_xfer_destroy,
326        prplcb_xfer_add,
327        prplcb_xfer_progress,
328        prplcb_xfer_dbg,
329        prplcb_xfer_cancel_remote,
330        NULL,
331        NULL,
332        prplcb_xfer_dbg,
333};
Note: See TracBrowser for help on using the repository browser.