source: protocols/purple/ft.c @ ce70e88

Last change on this file since ce70e88 was 5ebff60, checked in by dequis <dx@…>, at 2015-02-20T22:50:54Z

Reindent everything to K&R style with tabs

Used uncrustify, with the configuration file in ./doc/uncrustify.cfg

Commit author set to "Indent <please@…>" so that it's easier to
skip while doing git blame.

  • Property mode set to 100644
File size: 9.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        PurpleXfer *xfer;
38        file_transfer_t *ft;
39        struct im_connection *ic;
40        int fd;
41        char *fn, *handle;
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
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
65        purple_xfer_request_denied(px->xfer);
66}
67
68static void prplcb_xfer_new(PurpleXfer *xfer)
69{
70        if (purple_xfer_get_type(xfer) == PURPLE_XFER_RECEIVE) {
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                px->ic = purple_ic_by_pa(xfer->account);
78
79                purple_xfer_set_local_filename(xfer, px->fn);
80
81                /* Sadly the xfer struct is still empty ATM so come back after
82                   the caller is done. */
83                b_timeout_add(0, prplcb_xfer_new_send_cb, xfer);
84        } else {
85                struct file_transfer *ft = next_ft;
86                struct prpl_xfer_data *px = ft->data;
87
88                xfer->ui_data = px;
89                px->xfer = xfer;
90
91                next_ft = NULL;
92        }
93}
94
95static gboolean prplcb_xfer_new_send_cb(gpointer data, gint fd, b_input_condition cond)
96{
97        PurpleXfer *xfer = data;
98        struct im_connection *ic = purple_ic_by_pa(xfer->account);
99        struct prpl_xfer_data *px = xfer->ui_data;
100        PurpleBuddy *buddy;
101        const char *who;
102
103        buddy = purple_find_buddy(xfer->account, xfer->who);
104        who = buddy ? purple_buddy_get_name(buddy) : xfer->who;
105
106        /* TODO(wilmer): After spreading some more const goodness in BitlBee,
107           remove the evil cast below. */
108        px->ft = imcb_file_send_start(ic, (char *) who, xfer->filename, xfer->size);
109        px->ft->data = px;
110
111        px->ft->accept = prpl_xfer_accept;
112        px->ft->canceled = prpl_xfer_canceled;
113        px->ft->write_request = prpl_xfer_write_request;
114
115        return FALSE;
116}
117
118gboolean try_write_to_ui(gpointer data, gint fd, b_input_condition cond)
119{
120        struct file_transfer *ft = data;
121        struct prpl_xfer_data *px = ft->data;
122        struct stat fs;
123        off_t tx_bytes;
124
125        /* If we don't have the file opened yet, there's no data so wait. */
126        if (px->fd < 0 || !px->ui_wants_data) {
127                return FALSE;
128        }
129
130        tx_bytes = lseek(px->fd, 0, SEEK_CUR);
131        fstat(px->fd, &fs);
132
133        if (fs.st_size > tx_bytes) {
134                char buf[1024];
135                size_t n = MIN(fs.st_size - tx_bytes, sizeof(buf));
136
137                if (read(px->fd, buf, n) == n && ft->write(ft, buf, n)) {
138                        px->ui_wants_data = FALSE;
139                } else {
140                        purple_xfer_cancel_local(px->xfer);
141                        imcb_file_canceled(px->ic, ft, "Read error");
142                }
143        }
144
145        if (lseek(px->fd, 0, SEEK_CUR) == px->xfer->size) {
146                /*purple_xfer_end( px->xfer );*/
147                imcb_file_finished(px->ic, ft);
148        }
149
150        return FALSE;
151}
152
153/* UI calls this when its buffer is empty and wants more data to send to the user. */
154static gboolean prpl_xfer_write_request(struct file_transfer *ft)
155{
156        struct prpl_xfer_data *px = ft->data;
157
158        px->ui_wants_data = TRUE;
159        try_write_to_ui(ft, 0, 0);
160
161        return FALSE;
162}
163
164
165/* Generic (IM<>UI): */
166static void prplcb_xfer_destroy(PurpleXfer *xfer)
167{
168        struct prpl_xfer_data *px = xfer->ui_data;
169
170        g_free(px->fn);
171        g_free(px->handle);
172        if (px->fd >= 0) {
173                close(px->fd);
174        }
175        g_free(px);
176}
177
178static void prplcb_xfer_progress(PurpleXfer *xfer, double percent)
179{
180        struct prpl_xfer_data *px = xfer->ui_data;
181
182        if (px == NULL) {
183                return;
184        }
185
186        if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND) {
187                if (*px->fn) {
188                        char *slash;
189
190                        unlink(px->fn);
191                        if ((slash = strrchr(px->fn, '/'))) {
192                                *slash = '\0';
193                                rmdir(px->fn);
194                        }
195                        *px->fn = '\0';
196                }
197
198                return;
199        }
200
201        if (px->fd == -1 && percent > 0) {
202                /* Weeeeeeeee, we're getting data! That means the file exists
203                   by now so open it and start sending to the UI. */
204                px->fd = open(px->fn, O_RDONLY);
205
206                /* Unlink it now, because we don't need it after this. */
207                unlink(px->fn);
208        }
209
210        if (percent < 1) {
211                try_write_to_ui(px->ft, 0, 0);
212        } else {
213                /* Another nice problem: If we have the whole file, it only
214                   gets closed when we return. Problem: There may still be
215                   stuff buffered and not written, we'll only see it after
216                   the caller close()s the file. So poll the file after that. */
217                b_timeout_add(0, try_write_to_ui, px->ft);
218        }
219}
220
221static void prplcb_xfer_cancel_remote(PurpleXfer *xfer)
222{
223        struct prpl_xfer_data *px = xfer->ui_data;
224
225        if (px->ft) {
226                imcb_file_canceled(px->ic, px->ft, "Canceled by remote end");
227        } else {
228                /* px->ft == NULL for sends, because of the two stages. :-/ */
229                imcb_error(px->ic, "File transfer cancelled by remote end");
230        }
231}
232
233static void prplcb_xfer_dbg(PurpleXfer *xfer)
234{
235        fprintf(stderr, "prplcb_xfer_dbg 0x%p\n", xfer);
236}
237
238
239/* Sending files (UI->IM): */
240static gboolean prpl_xfer_write(struct file_transfer *ft, char *buffer, unsigned int len);
241static gboolean purple_transfer_request_cb(gpointer data, gint fd, b_input_condition cond);
242
243void purple_transfer_request(struct im_connection *ic, file_transfer_t *ft, char *handle)
244{
245        struct prpl_xfer_data *px = g_new0(struct prpl_xfer_data, 1);
246        char *dir, *basename;
247
248        ft->data = px;
249        px->ft = ft;
250
251        dir = g_strdup("/tmp/bitlbee-purple-ft.XXXXXX");
252        if (!mkdtemp(dir)) {
253                imcb_error(ic, "Could not create temporary file for file transfer");
254                g_free(px);
255                g_free(dir);
256                return;
257        }
258
259        if ((basename = strrchr(ft->file_name, '/'))) {
260                basename++;
261        } else {
262                basename = ft->file_name;
263        }
264        px->fn = g_strdup_printf("%s/%s", dir, basename);
265        px->fd = open(px->fn, O_WRONLY | O_CREAT, 0600);
266        g_free(dir);
267
268        if (px->fd < 0) {
269                imcb_error(ic, "Could not create temporary file for file transfer");
270                g_free(px);
271                g_free(px->fn);
272                return;
273        }
274
275        px->ic = ic;
276        px->handle = g_strdup(handle);
277
278        imcb_log(ic,
279                 "Due to libpurple limitations, the file has to be cached locally before proceeding with the actual file transfer. Please wait...");
280
281        b_timeout_add(0, purple_transfer_request_cb, ft);
282}
283
284static void purple_transfer_forward(struct file_transfer *ft)
285{
286        struct prpl_xfer_data *px = ft->data;
287        PurpleAccount *pa = px->ic->proto_data;
288
289        /* xfer_new() will pick up this variable. It's a hack but we're not
290           multi-threaded anyway. */
291        next_ft = ft;
292        serv_send_file(purple_account_get_connection(pa), px->handle, px->fn);
293}
294
295static gboolean purple_transfer_request_cb(gpointer data, gint fd, b_input_condition cond)
296{
297        file_transfer_t *ft = data;
298        struct prpl_xfer_data *px = ft->data;
299
300        if (ft->write == NULL) {
301                ft->write = prpl_xfer_write;
302                imcb_file_recv_start(px->ic, ft);
303        }
304
305        ft->write_request(ft);
306
307        return FALSE;
308}
309
310static gboolean prpl_xfer_write(struct file_transfer *ft, char *buffer, unsigned int len)
311{
312        struct prpl_xfer_data *px = ft->data;
313
314        if (write(px->fd, buffer, len) != len) {
315                imcb_file_canceled(px->ic, ft, "Error while writing temporary file");
316                return FALSE;
317        }
318
319        if (lseek(px->fd, 0, SEEK_CUR) >= ft->file_size) {
320                close(px->fd);
321                px->fd = -1;
322
323                purple_transfer_forward(ft);
324                imcb_file_finished(px->ic, ft);
325                px->ft = NULL;
326        } else {
327                b_timeout_add(0, purple_transfer_request_cb, ft);
328        }
329
330        return TRUE;
331}
332
333
334
335PurpleXferUiOps bee_xfer_uiops =
336{
337        prplcb_xfer_new,
338        prplcb_xfer_destroy,
339        NULL, /* prplcb_xfer_add, */
340        prplcb_xfer_progress,
341        prplcb_xfer_dbg,
342        prplcb_xfer_cancel_remote,
343        NULL,
344        NULL,
345        prplcb_xfer_dbg,
346};
Note: See TracBrowser for help on using the repository browser.