OBS: update support for CentOS & Scientific Linux 7
[siplcs.git] / src / purple / purple-ft.c
blobc5b5aa1093edea7a888a622f88cfac692637daff
1 /**
2 * @file purple-ft.c
4 * pidgin-sipe
6 * Copyright (C) 2010-2017 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 g_timeout_add(t, f, d) purple_timeout_add(t, f, d)
46 #define PurpleXferStatus PurpleXferStatusType
47 #define PURPLE_XFER_TO_SIPE_CORE_PUBLIC ((struct sipe_core_public *) purple_account_get_connection(xfer->account)->proto_data)
48 #define PURPLE_XFER_TYPE_RECEIVE PURPLE_XFER_RECEIVE
49 #define PURPLE_XFER_TYPE_SEND PURPLE_XFER_SEND
50 #define purple_xfer_get_fd(xfer) xfer->fd
51 #define purple_xfer_get_protocol_data(xfer) xfer->data
52 #define purple_xfer_get_status(xfer) purple_xfer_get_status(xfer)
53 #define purple_xfer_get_xfer_type(xfer) purple_xfer_get_type(xfer)
54 #define purple_xfer_get_watcher(xfer) xfer->watcher
55 #define purple_xfer_set_protocol_data(xfer, d) xfer->data = d
56 #define purple_xfer_set_watcher(xfer, w) xfer->watcher = w
57 #endif
59 #ifdef _WIN32
60 /* wrappers for write() & friends for socket handling */
61 #include "win32/win32dep.h"
62 #endif
64 #include "sipe-common.h"
65 #include "sipe-backend.h"
66 #include "sipe-core.h"
68 #include "purple-private.h"
70 #define FT_TO_PURPLE_XFER ((PurpleXfer *) ft->backend_private)
71 #define PURPLE_XFER_TO_SIPE_FILE_TRANSFER ((struct sipe_file_transfer *) purple_xfer_get_protocol_data(xfer))
73 void sipe_backend_ft_error(struct sipe_file_transfer *ft,
74 const char *errmsg)
76 PurpleXfer *xfer = FT_TO_PURPLE_XFER;
77 purple_xfer_error(purple_xfer_get_xfer_type(xfer),
78 purple_xfer_get_account(xfer),
79 purple_xfer_get_remote_user(xfer),
80 errmsg);
83 const gchar *sipe_backend_ft_get_error(SIPE_UNUSED_PARAMETER struct sipe_file_transfer *ft)
85 return strerror(errno);
88 void sipe_backend_ft_deallocate(struct sipe_file_transfer *ft)
90 PurpleXfer *xfer = FT_TO_PURPLE_XFER;
91 PurpleXferStatus status = purple_xfer_get_status(xfer);
93 // If file transfer is not finished, cancel it
94 if ( status != PURPLE_XFER_STATUS_DONE
95 && status != PURPLE_XFER_STATUS_CANCEL_LOCAL
96 && status != PURPLE_XFER_STATUS_CANCEL_REMOTE) {
97 purple_xfer_set_cancel_recv_fnc(xfer, NULL);
98 purple_xfer_set_cancel_send_fnc(xfer, NULL);
99 purple_xfer_cancel_remote(xfer);
103 gssize sipe_backend_ft_read(struct sipe_file_transfer *ft,
104 guchar *data,
105 gsize size)
107 gssize bytes_read = read(purple_xfer_get_fd(FT_TO_PURPLE_XFER),
108 data,
109 size);
110 if (bytes_read == 0) {
111 /* Sender canceled transfer before it was finished */
112 return -2;
113 } else if (bytes_read == -1) {
114 if (errno == EAGAIN)
115 return 0;
116 else
117 return -1;
119 return bytes_read;
122 gssize sipe_backend_ft_write(struct sipe_file_transfer *ft,
123 const guchar *data,
124 gsize size)
126 gssize bytes_written = write(purple_xfer_get_fd(FT_TO_PURPLE_XFER),
127 data,
128 size);
129 if (bytes_written == -1) {
130 if (errno == EAGAIN)
131 return 0;
132 else
133 return -1;
135 return bytes_written;
138 static gboolean
139 end_transfer_cb(gpointer data)
141 purple_xfer_end((PurpleXfer *)data);
142 return FALSE;
145 void
146 sipe_backend_ft_set_completed(struct sipe_file_transfer *ft)
148 purple_xfer_set_completed(FT_TO_PURPLE_XFER, TRUE);
149 g_timeout_add(0, end_transfer_cb, FT_TO_PURPLE_XFER);
152 void sipe_backend_ft_cancel_local(struct sipe_file_transfer *ft)
154 purple_xfer_cancel_local(FT_TO_PURPLE_XFER);
157 void sipe_backend_ft_cancel_remote(struct sipe_file_transfer *ft)
159 purple_xfer_cancel_remote(FT_TO_PURPLE_XFER);
162 static void
163 ft_free_xfer_struct(PurpleXfer *xfer)
165 if (purple_xfer_get_watcher(xfer)) {
166 purple_input_remove(purple_xfer_get_watcher(xfer));
167 purple_xfer_set_watcher(xfer, 0);
170 purple_xfer_set_protocol_data(xfer, NULL);
173 static void
174 ft_request_denied(PurpleXfer *xfer)
176 struct sipe_file_transfer *ft = PURPLE_XFER_TO_SIPE_FILE_TRANSFER;
177 if (ft->ft_request_denied) {
178 ft->ft_request_denied(ft);
181 ft_free_xfer_struct(xfer);
184 static void
185 ft_cancelled(PurpleXfer *xfer)
187 struct sipe_file_transfer *ft = PURPLE_XFER_TO_SIPE_FILE_TRANSFER;
189 if (ft->ft_cancelled &&
190 purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_CANCEL_LOCAL) {
191 ft->ft_cancelled(ft);
194 ft_free_xfer_struct(xfer);
197 static void
198 ft_init(PurpleXfer *xfer)
200 struct sipe_file_transfer *ft = PURPLE_XFER_TO_SIPE_FILE_TRANSFER;
201 g_return_if_fail(ft->ft_init);
203 ft->ft_init(ft,
204 purple_xfer_get_filename(xfer),
205 purple_xfer_get_size(xfer),
206 purple_xfer_get_remote_user(xfer));
209 static void
210 ft_start(PurpleXfer *xfer)
212 struct sipe_file_transfer *ft = PURPLE_XFER_TO_SIPE_FILE_TRANSFER;
214 if (purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_RECEIVE) {
215 /* Set socket to non-blocking mode */
216 int flags = fcntl(purple_xfer_get_fd(xfer), F_GETFL, 0);
217 if (flags == -1) {
218 flags = 0;
220 /* @TODO: ignoring potential error return - how to handle? */
221 (void) fcntl(purple_xfer_get_fd(xfer), F_SETFL, flags | O_NONBLOCK);
224 if (ft->ft_start) {
225 ft->ft_start(ft, purple_xfer_get_size(xfer));
229 static void
230 ft_end(PurpleXfer *xfer)
232 struct sipe_file_transfer *ft = PURPLE_XFER_TO_SIPE_FILE_TRANSFER;
234 if (!ft->ft_end || ft->ft_end(ft)) {
235 /* We're done with this transfer */
236 ft_free_xfer_struct(xfer);
237 } else if (purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_RECEIVE) {
238 /* Remove incomplete file from failed transfer. */
239 unlink(purple_xfer_get_local_filename(xfer));
243 static gssize
244 ft_read(guchar **buffer,
245 #if PURPLE_VERSION_CHECK(3,0,0)
246 size_t buffer_size,
247 #endif
248 PurpleXfer *xfer)
250 struct sipe_file_transfer *ft = PURPLE_XFER_TO_SIPE_FILE_TRANSFER;
251 g_return_val_if_fail(ft->ft_read, 0);
252 return ft->ft_read(ft, buffer, purple_xfer_get_bytes_remaining(xfer),
253 #if PURPLE_VERSION_CHECK(3,0,0)
254 buffer_size
255 #else
256 xfer->current_buffer_size
257 #endif
261 static gssize
262 ft_write(const guchar *buffer, size_t size, PurpleXfer *xfer)
264 struct sipe_file_transfer *ft = PURPLE_XFER_TO_SIPE_FILE_TRANSFER;
265 gssize bytes_written = 0;
267 g_return_val_if_fail(ft->ft_write, 0);
269 bytes_written = ft->ft_write(ft, buffer, size);
271 if ((purple_xfer_get_bytes_remaining(xfer) - bytes_written) == 0)
272 purple_xfer_set_completed(xfer, TRUE);
274 return bytes_written;
277 static PurpleXfer *
278 create_xfer(PurpleAccount *account, PurpleXferType type, const char *who,
279 struct sipe_file_transfer *ft)
281 PurpleXfer *xfer = purple_xfer_new(account, type, who);
282 if (xfer) {
283 ft->backend_private = (struct sipe_backend_file_transfer *)xfer;
285 purple_xfer_set_protocol_data(xfer, ft);
286 purple_xfer_set_init_fnc(xfer, ft_init);
287 purple_xfer_set_request_denied_fnc(xfer, ft_request_denied);
288 purple_xfer_set_cancel_send_fnc(xfer, ft_cancelled);
289 purple_xfer_set_cancel_recv_fnc(xfer, ft_cancelled);
290 purple_xfer_set_start_fnc(xfer, ft_start);
291 purple_xfer_set_end_fnc(xfer, ft_end);
294 return xfer;
297 void sipe_backend_ft_incoming(struct sipe_core_public *sipe_public,
298 struct sipe_file_transfer *ft,
299 const gchar *who,
300 const gchar *file_name,
301 gsize file_size)
303 struct sipe_backend_private *purple_private = sipe_public->backend_private;
304 PurpleXfer *xfer = create_xfer(purple_private->account,
305 PURPLE_XFER_TYPE_RECEIVE, who, ft);
306 if (xfer) {
307 purple_xfer_set_filename(xfer, file_name);
308 purple_xfer_set_size(xfer, file_size);
310 purple_xfer_request(xfer);
314 void
315 sipe_backend_ft_outgoing(struct sipe_core_public *sipe_public,
316 struct sipe_file_transfer *ft,
317 const gchar *who,
318 const gchar *file_name)
320 struct sipe_backend_private *purple_private = sipe_public->backend_private;
321 PurpleXfer *xfer = create_xfer(purple_private->account,
322 PURPLE_XFER_TYPE_SEND, who, ft);
323 if (xfer) {
324 if (file_name != NULL)
325 purple_xfer_request_accepted(xfer, file_name);
326 else
327 purple_xfer_request(xfer);
331 static void
332 connect_cb(gpointer data, gint fd, SIPE_UNUSED_PARAMETER const gchar *error_message)
334 struct sipe_file_transfer *ft = data;
336 if (fd < 0) {
337 purple_xfer_cancel_local(FT_TO_PURPLE_XFER);
338 return;
341 purple_xfer_start(FT_TO_PURPLE_XFER, fd, NULL, 0);
344 void
345 sipe_backend_ft_start(struct sipe_file_transfer *ft, struct sipe_backend_fd *fd,
346 const char* ip, unsigned port)
348 PurpleXferType type = purple_xfer_get_xfer_type(FT_TO_PURPLE_XFER);
349 if (type == PURPLE_XFER_TYPE_SEND && ft->ft_write) {
350 purple_xfer_set_write_fnc(FT_TO_PURPLE_XFER, ft_write);
351 } else if (type == PURPLE_XFER_TYPE_RECEIVE && ft->ft_read) {
352 purple_xfer_set_read_fnc(FT_TO_PURPLE_XFER, ft_read);
355 if (ip && port && !sipe_backend_ft_is_incoming(ft)) {
356 /* Purple accepts ip & port only for incoming file transfers.
357 * If we want to send file with Sender-Connect = TRUE negotiated,
358 * we have to open the connection ourselves and pass the file
359 * descriptor to purple_xfer_start. */
360 purple_proxy_connect(NULL,
361 purple_xfer_get_account(FT_TO_PURPLE_XFER),
363 port,
364 connect_cb,
365 ft);
366 return;
369 purple_xfer_start(FT_TO_PURPLE_XFER, fd ? fd->fd : -1, ip, port);
372 void sipe_purple_ft_send_file(PurpleConnection *gc,
373 const char *who,
374 const char *file)
376 sipe_core_ft_create_outgoing(PURPLE_GC_TO_SIPE_CORE_PUBLIC, who, file);
379 gboolean
380 sipe_backend_ft_is_incoming(struct sipe_file_transfer *ft)
382 return(purple_xfer_get_xfer_type(FT_TO_PURPLE_XFER) == PURPLE_XFER_TYPE_RECEIVE);
386 Local Variables:
387 mode: c
388 c-file-style: "bsd"
389 indent-tabs-mode: t
390 tab-width: 8
391 End: