source: protocols/purple/ft.c @ c96c72f

Last change on this file since c96c72f was c96c72f, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-05-18T00:08:17Z

Little cleanup. Less compiler warnings, and removing tempfile at the
beginning of the download already to make sure it doesn't stick around.

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