source: protocols/purple/ft.c @ 5d1b3a95

Last change on this file since 5d1b3a95 was 8822d23, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-05-17T23:23:20Z

This receives files but is very fragile if anything unusual happens (like a
cancellation/timeout/etc).

  • Property mode set to 100644
File size: 7.2 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        int fd;
41        char *fn;
42        gboolean ui_wants_data;
43};
44
45static file_transfer_t *next_ft;
46
47struct im_connection *purple_ic_by_pa( PurpleAccount *pa );
48
49gboolean try_write_to_ui( gpointer data, gint fd, b_input_condition cond )
50{
51        struct file_transfer *ft = data;
52        struct prpl_xfer_data *px = ft->data;
53        struct stat fs;
54        off_t tx_bytes;
55       
56        fprintf( stderr, "write_to_ui\n" );
57       
58        /* If we don't have the file opened yet, there's no data so wait. */
59        if( px->fd < 0 || !px->ui_wants_data )
60                return FALSE;
61       
62        tx_bytes = lseek( px->fd, 0, SEEK_CUR );
63        fstat( px->fd, &fs );
64       
65        fprintf( stderr, "write_to_ui %zd %zd %zd\n", fs.st_size, tx_bytes, px->xfer->size );
66       
67        if( fs.st_size > tx_bytes )
68        {
69                char buf[1024];
70                size_t n = MIN( fs.st_size - tx_bytes, sizeof( buf ) );
71               
72                if( read( px->fd, buf, n ) == n && ft->write( ft, buf, n ) )
73                {
74                        fprintf( stderr, "Wrote %zd bytes\n", n );
75                        px->ui_wants_data = FALSE;
76                }
77                else
78                {
79                        purple_xfer_cancel_local( px->xfer );
80                        imcb_file_canceled( ft, "Read error" );
81                }
82        }
83       
84        if( lseek( px->fd, 0, SEEK_CUR ) == px->xfer->size )
85        {
86                purple_xfer_end( px->xfer );
87                imcb_file_finished( ft );
88        }
89       
90        return FALSE;
91}
92
93/* UI calls this when its buffer is empty and wants more data to send to the user. */
94static gboolean prpl_xfer_write_request( struct file_transfer *ft )
95{
96        struct prpl_xfer_data *px = ft->data;
97       
98        fprintf( stderr, "wrq\n" );
99       
100        px->ui_wants_data = TRUE;
101        try_write_to_ui( ft, 0, 0 );
102       
103        return FALSE;
104}
105
106static gboolean prpl_xfer_write( struct file_transfer *ft, char *buffer, unsigned int len )
107{
108        return FALSE;
109}
110
111static void prpl_xfer_accept( struct file_transfer *ft )
112{
113        struct prpl_xfer_data *px = ft->data;
114        purple_xfer_request_accepted( px->xfer, NULL );
115        prpl_xfer_write_request( ft );
116}
117
118static void prpl_xfer_canceled( struct file_transfer *ft, char *reason )
119{
120        struct prpl_xfer_data *px = ft->data;
121        purple_xfer_request_denied( px->xfer );
122}
123
124static gboolean prplcb_xfer_new_send_cb( gpointer data, gint fd, b_input_condition cond );
125
126static void prplcb_xfer_new( PurpleXfer *xfer )
127{
128        if( purple_xfer_get_type( xfer ) == PURPLE_XFER_RECEIVE )
129        {
130                struct prpl_xfer_data *px = g_new0( struct prpl_xfer_data, 1 );
131               
132                xfer->ui_data = px;
133                px->xfer = xfer;
134                px->fn = mktemp( g_strdup( "/tmp/bitlbee-purple-ft.XXXXXX" ) );
135                px->fd = -1;
136               
137                purple_xfer_set_local_filename( xfer, px->fn );
138               
139                /* Sadly the xfer struct is still empty ATM so come back after
140                   the caller is done. */
141                b_timeout_add( 0, prplcb_xfer_new_send_cb, xfer );
142        }
143        else
144        {
145                /*
146                struct prpl_xfer_data *px = g_new0( struct prpl_xfer_data, 1 );
147               
148                px->fd = -1;
149                px->ft = next_ft;
150                px->ft->data = px;
151                px->xfer = xfer;
152                px->xfer->ui_data = px;
153               
154                purple_xfer_set_filename( xfer, px->ft->file_name );
155                purple_xfer_set_size( xfer, px->ft->file_size );
156               
157                next_ft = NULL;
158                */
159        }
160}
161
162static gboolean prplcb_xfer_new_send_cb( gpointer data, gint fd, b_input_condition cond )
163{
164        PurpleXfer *xfer = data;
165        struct im_connection *ic = purple_ic_by_pa( xfer->account );
166        struct prpl_xfer_data *px = xfer->ui_data;
167        PurpleBuddy *buddy;
168        const char *who;
169       
170        buddy = purple_find_buddy( xfer->account, xfer->who );
171        who = buddy ? purple_buddy_get_name( buddy ) : xfer->who;
172       
173        /* TODO(wilmer): After spreading some more const goodness in BitlBee,
174           remove the evil cast below. */
175        px->ft = imcb_file_send_start( ic, (char*) who, xfer->filename, xfer->size );
176        px->ft->data = px;
177       
178        px->ft->accept = prpl_xfer_accept;
179        px->ft->canceled = prpl_xfer_canceled;
180        px->ft->write_request = prpl_xfer_write_request;
181       
182        return FALSE;
183}
184
185static void prplcb_xfer_destroy( PurpleXfer *xfer )
186{
187        struct prpl_xfer_data *px = xfer->ui_data;
188       
189        g_free( px->fn );
190        if( px->fd >= 0 )
191                close( px->fd );
192        g_free( px );
193}
194
195static void prplcb_xfer_progress( PurpleXfer *xfer, double percent )
196{
197        struct prpl_xfer_data *px = xfer->ui_data;
198       
199        fprintf( stderr, "prplcb_xfer_progress 0x%p %f\n", xfer, percent );
200       
201        if( px->fd == -1 && percent > 0 )
202        {
203                /* Weeeeeeeee, we're getting data! That means the file exists
204                   by now so open it and start sending to the UI. */
205                px->fd = open( px->fn, O_RDONLY );
206               
207                /* Unlink it now, because we don't need it after this. */
208                //unlink( px->fn );
209        }
210       
211        if( percent < 1 )
212                try_write_to_ui( px->ft, 0, 0 );
213        else
214                b_timeout_add( 0, try_write_to_ui, px->ft );
215}
216
217static void prplcb_xfer_cancel_remote( PurpleXfer *xfer )
218{
219        struct prpl_xfer_data *px = xfer->ui_data;
220       
221        imcb_file_canceled( px->ft, "Canceled by remote end" );
222}
223
224static void prplcb_xfer_dbg( PurpleXfer *xfer )
225{
226        fprintf( stderr, "prplcb_xfer_dbg 0x%p\n", xfer );
227}
228
229PurpleXferUiOps bee_xfer_uiops =
230{
231        prplcb_xfer_new,
232        prplcb_xfer_destroy,
233        prplcb_xfer_dbg,
234        prplcb_xfer_progress,
235        prplcb_xfer_dbg,
236        prplcb_xfer_cancel_remote,
237        NULL,
238        NULL,
239        prplcb_xfer_dbg,
240};
241
242void purple_transfer_request( struct im_connection *ic, file_transfer_t *ft, char *handle )
243{
244        PurpleAccount *pa = ic->proto_data;
245        struct prpl_xfer_data *px;
246       
247        /* xfer_new() will pick up this variable. It's a hack but we're not
248           multi-threaded anyway. */
249        next_ft = ft;
250        serv_send_file( purple_account_get_connection( pa ), handle, ft->file_name );
251       
252        ft->write = prpl_xfer_write;
253       
254        px = ft->data;
255        imcb_file_recv_start( ft );
256}
Note: See TracBrowser for help on using the repository browser.