filetransfer: use 'end' callback in sipe_file_transfer
[siplcs.git] / src / purple / purple-ft.c
blobc93a68952a25e6acbcfeea0bebdcbfad0bb716fe
1 /**
2 * @file purple-ft.c
4 * pidgin-sipe
6 * Copyright (C) 2010-2015 SIPE Project <http://sipe.sourceforge.net/>
7 * Copyright (C) 2010 Jakub Adam <jakub.adam@ktknet.cz>
8 * Copyright (C) 2010 Tomáš Hrabčík <tomas.hrabcik@tieto.com>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
29 #include <string.h>
30 #include <fcntl.h>
31 #include <errno.h>
33 #ifdef HAVE_UNISTD_H
34 #include <unistd.h>
35 #endif
37 #include <glib.h>
39 #include "version.h"
40 #if PURPLE_VERSION_CHECK(3,0,0)
41 #include "protocol.h"
42 #define PURPLE_XFER_TO_SIPE_CORE_PUBLIC ((struct sipe_core_public *) purple_connection_get_protocol_data(purple_account_get_connection(purple_xfer_get_account(xfer))))
43 #else
44 #include "ft.h"
45 #define PurpleXferStatus PurpleXferStatusType
46 #define PURPLE_XFER_TO_SIPE_CORE_PUBLIC ((struct sipe_core_public *) purple_account_get_connection(xfer->account)->proto_data)
47 #define PURPLE_XFER_TYPE_RECEIVE PURPLE_XFER_RECEIVE
48 #define PURPLE_XFER_TYPE_SEND PURPLE_XFER_SEND
49 #define purple_xfer_get_fd(xfer) xfer->fd
50 #define purple_xfer_get_protocol_data(xfer) xfer->data
51 #define purple_xfer_get_status(xfer) purple_xfer_get_status(xfer)
52 #define purple_xfer_get_xfer_type(xfer) purple_xfer_get_type(xfer)
53 #define purple_xfer_get_watcher(xfer) xfer->watcher
54 #define purple_xfer_set_protocol_data(xfer, d) xfer->data = d
55 #define purple_xfer_set_watcher(xfer, w) xfer->watcher = w
56 #endif
58 #ifdef _WIN32
59 /* wrappers for write() & friends for socket handling */
60 #include "win32/win32dep.h"
61 #endif
63 #include "sipe-common.h"
64 #include "sipe-backend.h"
65 #include "sipe-core.h"
67 #include "purple-private.h"
69 #define FT_TO_PURPLE_XFER ((PurpleXfer *) ft->backend_private)
70 #define PURPLE_XFER_TO_SIPE_FILE_TRANSFER ((struct sipe_file_transfer *) purple_xfer_get_protocol_data(xfer))
72 void sipe_backend_ft_error(struct sipe_file_transfer *ft,
73 const char *errmsg)
75 PurpleXfer *xfer = FT_TO_PURPLE_XFER;
76 purple_xfer_error(purple_xfer_get_xfer_type(xfer),
77 purple_xfer_get_account(xfer),
78 purple_xfer_get_remote_user(xfer),
79 errmsg);
82 const gchar *sipe_backend_ft_get_error(SIPE_UNUSED_PARAMETER struct sipe_file_transfer *ft)
84 return strerror(errno);
87 void sipe_backend_ft_deallocate(struct sipe_file_transfer *ft)
89 PurpleXfer *xfer = FT_TO_PURPLE_XFER;
90 PurpleXferStatus status = purple_xfer_get_status(xfer);
92 // If file transfer is not finished, cancel it
93 if ( status != PURPLE_XFER_STATUS_DONE
94 && status != PURPLE_XFER_STATUS_CANCEL_LOCAL
95 && status != PURPLE_XFER_STATUS_CANCEL_REMOTE) {
96 purple_xfer_set_cancel_recv_fnc(xfer, NULL);
97 purple_xfer_set_cancel_send_fnc(xfer, NULL);
98 purple_xfer_cancel_remote(xfer);
102 gssize sipe_backend_ft_read(struct sipe_file_transfer *ft,
103 guchar *data,
104 gsize size)
106 gssize bytes_read = read(purple_xfer_get_fd(FT_TO_PURPLE_XFER),
107 data,
108 size);
109 if (bytes_read == 0) {
110 /* Sender canceled transfer before it was finished */
111 return -2;
112 } else if (bytes_read == -1) {
113 if (errno == EAGAIN)
114 return 0;
115 else
116 return -1;
118 return bytes_read;
121 gssize sipe_backend_ft_write(struct sipe_file_transfer *ft,
122 const guchar *data,
123 gsize size)
125 gssize bytes_written = write(purple_xfer_get_fd(FT_TO_PURPLE_XFER),
126 data,
127 size);
128 if (bytes_written == -1) {
129 if (errno == EAGAIN)
130 return 0;
131 else
132 return -1;
134 return bytes_written;
137 void sipe_backend_ft_cancel_local(struct sipe_file_transfer *ft)
139 purple_xfer_cancel_local(FT_TO_PURPLE_XFER);
142 void sipe_backend_ft_cancel_remote(struct sipe_file_transfer *ft)
144 purple_xfer_cancel_remote(FT_TO_PURPLE_XFER);
147 static void
148 ft_free_xfer_struct(PurpleXfer *xfer)
150 struct sipe_file_transfer *ft = PURPLE_XFER_TO_SIPE_FILE_TRANSFER;
152 if (ft) {
153 if (purple_xfer_get_watcher(xfer)) {
154 purple_input_remove(purple_xfer_get_watcher(xfer));
155 purple_xfer_set_watcher(xfer, 0);
157 if (ft->deallocate) {
158 ft->deallocate(ft);
160 purple_xfer_set_protocol_data(xfer, NULL);
164 static void
165 ft_request_denied(PurpleXfer *xfer)
167 struct sipe_file_transfer *ft = PURPLE_XFER_TO_SIPE_FILE_TRANSFER;
168 if (ft->request_denied) {
169 ft->request_denied(ft);
172 ft_free_xfer_struct(xfer);
175 static void
176 ft_init(PurpleXfer *xfer)
178 struct sipe_file_transfer *ft = PURPLE_XFER_TO_SIPE_FILE_TRANSFER;
179 g_return_if_fail(ft->init);
181 ft->init(ft,
182 purple_xfer_get_filename(xfer),
183 purple_xfer_get_size(xfer),
184 purple_xfer_get_remote_user(xfer));
187 static void
188 ft_start(PurpleXfer *xfer)
190 struct sipe_file_transfer *ft = PURPLE_XFER_TO_SIPE_FILE_TRANSFER;
192 if (purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_RECEIVE) {
193 /* Set socket to non-blocking mode */
194 int flags = fcntl(purple_xfer_get_fd(xfer), F_GETFL, 0);
195 if (flags == -1) {
196 flags = 0;
198 /* @TODO: ignoring potential error return - how to handle? */
199 fcntl(purple_xfer_get_fd(xfer), F_SETFL, flags | O_NONBLOCK);
202 if (ft->start) {
203 ft->start(ft, purple_xfer_get_size(xfer));
207 static void
208 ft_end(PurpleXfer *xfer)
210 struct sipe_file_transfer *ft = PURPLE_XFER_TO_SIPE_FILE_TRANSFER;
212 if (ft->end && ft->end(ft)) {
213 /* We're done with this transfer */
214 ft_free_xfer_struct(xfer);
215 } else if (purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_RECEIVE) {
216 /* Remove incomplete file from failed transfer. */
217 unlink(purple_xfer_get_local_filename(xfer));
221 static gssize tftp_read(guchar **buffer,
222 #if PURPLE_VERSION_CHECK(3,0,0)
223 size_t buffer_size,
224 #endif
225 PurpleXfer *xfer)
227 return sipe_core_tftp_read(PURPLE_XFER_TO_SIPE_FILE_TRANSFER,
228 buffer,
229 purple_xfer_get_bytes_remaining(xfer),
230 #if PURPLE_VERSION_CHECK(3,0,0)
231 buffer_size
232 #else
233 xfer->current_buffer_size
234 #endif
238 static gssize
239 tftp_write(const guchar *buffer, size_t size, PurpleXfer *xfer)
241 gssize bytes_written = sipe_core_tftp_write(PURPLE_XFER_TO_SIPE_FILE_TRANSFER,
242 buffer, size);
244 if ((purple_xfer_get_bytes_remaining(xfer) - bytes_written) == 0)
245 purple_xfer_set_completed(xfer, TRUE);
247 return bytes_written;
250 //******************************************************************************
252 void sipe_backend_ft_incoming(struct sipe_core_public *sipe_public,
253 struct sipe_file_transfer *ft,
254 const gchar *who,
255 const gchar *file_name,
256 gsize file_size)
258 struct sipe_backend_private *purple_private = sipe_public->backend_private;
259 PurpleXfer *xfer;
261 xfer = purple_xfer_new(purple_private->account,
262 PURPLE_XFER_TYPE_RECEIVE,
263 who);
265 if (xfer) {
266 ft->backend_private = (struct sipe_backend_file_transfer *)xfer;
267 purple_xfer_set_protocol_data(xfer, ft);
269 purple_xfer_set_filename(xfer, file_name);
270 purple_xfer_set_size(xfer, file_size);
272 purple_xfer_set_init_fnc(xfer, ft_init);
273 purple_xfer_set_request_denied_fnc(xfer, ft_request_denied);
274 purple_xfer_set_cancel_send_fnc(xfer, ft_free_xfer_struct);
275 purple_xfer_set_cancel_recv_fnc(xfer, ft_free_xfer_struct);
276 purple_xfer_set_start_fnc(xfer, ft_start);
277 purple_xfer_set_end_fnc(xfer, ft_end);
278 purple_xfer_set_read_fnc(xfer, tftp_read);
280 purple_xfer_request(xfer);
284 static void
285 connect_cb(gpointer data, gint fd, SIPE_UNUSED_PARAMETER const gchar *error_message)
287 struct sipe_file_transfer *ft = data;
289 if (fd < 0) {
290 purple_xfer_cancel_local(FT_TO_PURPLE_XFER);
291 return;
294 purple_xfer_start(FT_TO_PURPLE_XFER, fd, NULL, 0);
297 void
298 sipe_backend_ft_start(struct sipe_file_transfer *ft, struct sipe_backend_fd *fd,
299 const char* ip, unsigned port)
301 if (ip && port && !sipe_backend_ft_is_incoming(ft)) {
302 /* Purple accepts ip & port only for incoming file transfers.
303 * If we want to send file with Sender-Connect = TRUE negotiated,
304 * we have to open the connection ourselves and pass the file
305 * descriptor to purple_xfer_start. */
306 purple_proxy_connect(NULL,
307 purple_xfer_get_account(FT_TO_PURPLE_XFER),
309 port,
310 connect_cb,
311 ft);
312 return;
315 purple_xfer_start(FT_TO_PURPLE_XFER, fd ? fd->fd : -1, ip, port);
318 void sipe_purple_ft_send_file(PurpleConnection *gc,
319 const char *who,
320 const char *file)
322 PurpleXfer *xfer = sipe_purple_ft_new_xfer(gc, who);
324 if (xfer) {
325 if (file != NULL)
326 purple_xfer_request_accepted(xfer, file);
327 else
328 purple_xfer_request(xfer);
332 PurpleXfer *sipe_purple_ft_new_xfer(PurpleConnection *gc, const char *who)
334 PurpleXfer *xfer = NULL;
336 #if !PURPLE_VERSION_CHECK(3,0,0)
337 if (!PURPLE_CONNECTION_IS_VALID(gc)) {
338 return NULL;
340 #endif
342 xfer = purple_xfer_new(purple_connection_get_account(gc),
343 PURPLE_XFER_TYPE_SEND,
344 who);
346 if (xfer) {
347 struct sipe_file_transfer *ft = sipe_core_ft_allocate(PURPLE_GC_TO_SIPE_CORE_PUBLIC);
349 ft->backend_private = (struct sipe_backend_file_transfer *)xfer;
350 purple_xfer_set_protocol_data(xfer, ft);
352 purple_xfer_set_init_fnc(xfer, ft_init);
353 purple_xfer_set_request_denied_fnc(xfer, ft_request_denied);
354 purple_xfer_set_cancel_send_fnc(xfer, ft_free_xfer_struct);
355 purple_xfer_set_cancel_recv_fnc(xfer, ft_free_xfer_struct);
356 purple_xfer_set_start_fnc(xfer, ft_start);
357 purple_xfer_set_end_fnc(xfer, ft_end);
358 purple_xfer_set_write_fnc(xfer, tftp_write);
361 return xfer;
364 gboolean
365 sipe_backend_ft_is_incoming(struct sipe_file_transfer *ft)
367 return(purple_xfer_get_xfer_type(FT_TO_PURPLE_XFER) == PURPLE_XFER_TYPE_RECEIVE);
371 Local Variables:
372 mode: c
373 c-file-style: "bsd"
374 indent-tabs-mode: t
375 tab-width: 8
376 End: