Fix some functions descriptions
[pidgin-git.git] / libpurple / xfer.c
blobfc1b6e5f37bd1b03ccfd61f8e3752713c882ef5b
1 /* purple
3 * Purple is the legal property of its developers, whose names are too numerous
4 * to list here. Please refer to the COPYRIGHT file distributed with this
5 * source distribution.
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
22 #include "internal.h"
23 #include "glibcompat.h"
25 #include "dbus-maybe.h"
26 #include "enums.h"
27 #include "image-store.h"
28 #include "xfer.h"
29 #include "network.h"
30 #include "notify.h"
31 #include "prefs.h"
32 #include "proxy.h"
33 #include "request.h"
34 #include "util.h"
35 #include "debug.h"
37 #define FT_INITIAL_BUFFER_SIZE 4096
38 #define FT_MAX_BUFFER_SIZE 65535
40 #define PURPLE_XFER_GET_PRIVATE(obj) \
41 (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_XFER, PurpleXferPrivate))
43 typedef struct _PurpleXferPrivate PurpleXferPrivate;
45 static PurpleXferUiOps *xfer_ui_ops = NULL;
46 static GList *xfers;
48 /* Private data for a file transfer */
49 struct _PurpleXferPrivate {
50 PurpleXferType type; /* The type of transfer. */
52 PurpleAccount *account; /* The account. */
54 char *who; /* The person on the other end of the
55 transfer. */
57 char *message; /* A message sent with the request */
58 char *filename; /* The name sent over the network. */
59 char *local_filename; /* The name on the local hard drive. */
60 goffset size; /* The size of the file. */
62 FILE *dest_fp; /* The destination file pointer. */
64 char *remote_ip; /* The remote IP address. */
65 guint16 local_port; /* The local port. */
66 guint16 remote_port; /* The remote port. */
68 int fd; /* The socket file descriptor. */
69 int watcher; /* Watcher. */
71 goffset bytes_sent; /* The number of bytes sent. */
72 time_t start_time; /* When the transfer of data began. */
73 time_t end_time; /* When the transfer of data ended. */
75 size_t current_buffer_size; /* This gradually increases for fast
76 network connections. */
78 PurpleXferStatus status; /* File Transfer's status. */
80 /* I/O operations, which should be set by the protocol using
81 * purple_xfer_set_init_fnc() and friends. Setting #init is
82 * mandatory; all others are optional.
84 struct
86 void (*init)(PurpleXfer *xfer);
87 void (*request_denied)(PurpleXfer *xfer);
88 void (*start)(PurpleXfer *xfer);
89 void (*end)(PurpleXfer *xfer);
90 void (*cancel_send)(PurpleXfer *xfer);
91 void (*cancel_recv)(PurpleXfer *xfer);
92 gssize (*read)(guchar **buffer, size_t size, PurpleXfer *xfer);
93 gssize (*write)(const guchar *buffer, size_t size, PurpleXfer *xfer);
94 void (*ack)(PurpleXfer *xfer, const guchar *buffer, size_t size);
95 } ops;
97 PurpleXferUiOps *ui_ops; /* UI-specific operations. */
99 /* TODO Remove this and use protocol-specific subclasses. */
100 void *proto_data; /* Protocol-specific data. */
103 * Used to moderate the file transfer when either the read/write ui_ops are
104 * set or fd is not set. In those cases, the UI/protocol call the respective
105 * function, which is somewhat akin to a fd watch being triggered.
107 enum {
108 PURPLE_XFER_READY_NONE = 0x0,
109 PURPLE_XFER_READY_UI = 0x1,
110 PURPLE_XFER_READY_PROTOCOL = 0x2,
111 } ready;
113 /* TODO: Should really use a PurpleCircBuffer for this. */
114 GByteArray *buffer;
116 gpointer thumbnail_data; /* thumbnail image */
117 gsize thumbnail_size;
118 gchar *thumbnail_mimetype;
121 /* GObject property enums */
122 enum
124 PROP_0,
125 PROP_TYPE,
126 PROP_ACCOUNT,
127 PROP_REMOTE_USER,
128 PROP_MESSAGE,
129 PROP_FILENAME,
130 PROP_LOCAL_FILENAME,
131 PROP_FILE_SIZE,
132 PROP_REMOTE_IP,
133 PROP_LOCAL_PORT,
134 PROP_REMOTE_PORT,
135 PROP_FD,
136 PROP_WATCHER,
137 PROP_BYTES_SENT,
138 PROP_START_TIME,
139 PROP_END_TIME,
140 PROP_STATUS,
141 PROP_LAST
144 static GObjectClass *parent_class;
145 static GParamSpec *properties[PROP_LAST];
147 static int purple_xfer_choose_file(PurpleXfer *xfer);
149 static const gchar *
150 purple_xfer_status_type_to_string(PurpleXferStatus type)
152 static const struct {
153 PurpleXferStatus type;
154 const char *name;
155 } type_names[] = {
156 { PURPLE_XFER_STATUS_UNKNOWN, "unknown" },
157 { PURPLE_XFER_STATUS_NOT_STARTED, "not started" },
158 { PURPLE_XFER_STATUS_ACCEPTED, "accepted" },
159 { PURPLE_XFER_STATUS_STARTED, "started" },
160 { PURPLE_XFER_STATUS_DONE, "done" },
161 { PURPLE_XFER_STATUS_CANCEL_LOCAL, "cancelled locally" },
162 { PURPLE_XFER_STATUS_CANCEL_REMOTE, "cancelled remotely" }
164 gsize i;
166 for (i = 0; i < G_N_ELEMENTS(type_names); ++i)
167 if (type_names[i].type == type)
168 return type_names[i].name;
170 return "invalid state";
173 void
174 purple_xfer_set_status(PurpleXfer *xfer, PurpleXferStatus status)
176 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
178 g_return_if_fail(priv != NULL);
180 if (purple_debug_is_verbose())
181 purple_debug_info("xfer", "Changing status of xfer %p from %s to %s\n",
182 xfer, purple_xfer_status_type_to_string(priv->status),
183 purple_xfer_status_type_to_string(status));
185 if (priv->status == status)
186 return;
188 priv->status = status;
190 g_object_notify_by_pspec(G_OBJECT(xfer), properties[PROP_STATUS]);
192 if(priv->type == PURPLE_XFER_TYPE_SEND) {
193 switch(status) {
194 case PURPLE_XFER_STATUS_ACCEPTED:
195 purple_signal_emit(purple_xfers_get_handle(), "file-send-accept", xfer);
196 break;
197 case PURPLE_XFER_STATUS_STARTED:
198 purple_signal_emit(purple_xfers_get_handle(), "file-send-start", xfer);
199 break;
200 case PURPLE_XFER_STATUS_DONE:
201 purple_signal_emit(purple_xfers_get_handle(), "file-send-complete", xfer);
202 break;
203 case PURPLE_XFER_STATUS_CANCEL_LOCAL:
204 case PURPLE_XFER_STATUS_CANCEL_REMOTE:
205 purple_signal_emit(purple_xfers_get_handle(), "file-send-cancel", xfer);
206 break;
207 default:
208 break;
210 } else if(priv->type == PURPLE_XFER_TYPE_RECEIVE) {
211 switch(status) {
212 case PURPLE_XFER_STATUS_ACCEPTED:
213 purple_signal_emit(purple_xfers_get_handle(), "file-recv-accept", xfer);
214 break;
215 case PURPLE_XFER_STATUS_STARTED:
216 purple_signal_emit(purple_xfers_get_handle(), "file-recv-start", xfer);
217 break;
218 case PURPLE_XFER_STATUS_DONE:
219 purple_signal_emit(purple_xfers_get_handle(), "file-recv-complete", xfer);
220 break;
221 case PURPLE_XFER_STATUS_CANCEL_LOCAL:
222 case PURPLE_XFER_STATUS_CANCEL_REMOTE:
223 purple_signal_emit(purple_xfers_get_handle(), "file-recv-cancel", xfer);
224 break;
225 default:
226 break;
231 static void
232 purple_xfer_conversation_write_internal(PurpleXfer *xfer,
233 const char *message, gboolean is_error, gboolean print_thumbnail)
235 PurpleIMConversation *im = NULL;
236 PurpleMessageFlags flags = PURPLE_MESSAGE_SYSTEM;
237 char *escaped;
238 gconstpointer thumbnail_data;
239 gsize size;
240 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
242 g_return_if_fail(priv != NULL);
243 g_return_if_fail(message != NULL);
245 thumbnail_data = purple_xfer_get_thumbnail(xfer, &size);
247 im = purple_conversations_find_im_with_account(priv->who,
248 purple_xfer_get_account(xfer));
250 if (im == NULL)
251 return;
253 escaped = g_markup_escape_text(message, -1);
255 if (is_error)
256 flags |= PURPLE_MESSAGE_ERROR;
258 if (print_thumbnail && thumbnail_data) {
259 gchar *message_with_img;
260 gpointer data = g_memdup(thumbnail_data, size);
261 PurpleImage *img;
262 guint id;
264 img = purple_image_new_from_data(data, size);
265 id = purple_image_store_add(img);
266 g_object_unref(img);
268 message_with_img = g_strdup_printf("<img src=\""
269 PURPLE_IMAGE_STORE_PROTOCOL "%u\"> %s", id, escaped);
270 purple_conversation_write_system_message(
271 PURPLE_CONVERSATION(im), message_with_img, flags);
272 g_free(message_with_img);
273 } else {
274 purple_conversation_write_system_message(
275 PURPLE_CONVERSATION(im), escaped, flags);
277 g_free(escaped);
280 void
281 purple_xfer_conversation_write(PurpleXfer *xfer, const gchar *message,
282 gboolean is_error)
284 purple_xfer_conversation_write_internal(xfer, message, is_error, FALSE);
287 /* maybe this one should be exported publically? */
288 static void
289 purple_xfer_conversation_write_with_thumbnail(PurpleXfer *xfer,
290 const gchar *message)
292 purple_xfer_conversation_write_internal(xfer, message, FALSE, TRUE);
296 static void purple_xfer_show_file_error(PurpleXfer *xfer, const char *filename)
298 int err = errno;
299 gchar *msg = NULL, *utf8;
300 PurpleXferType xfer_type = purple_xfer_get_xfer_type(xfer);
301 PurpleAccount *account = purple_xfer_get_account(xfer);
302 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
304 utf8 = g_filename_to_utf8(filename, -1, NULL, NULL, NULL);
305 switch(xfer_type) {
306 case PURPLE_XFER_TYPE_SEND:
307 msg = g_strdup_printf(_("Error reading %s: \n%s.\n"),
308 utf8, g_strerror(err));
309 break;
310 case PURPLE_XFER_TYPE_RECEIVE:
311 msg = g_strdup_printf(_("Error writing %s: \n%s.\n"),
312 utf8, g_strerror(err));
313 break;
314 default:
315 msg = g_strdup_printf(_("Error accessing %s: \n%s.\n"),
316 utf8, g_strerror(err));
317 break;
319 g_free(utf8);
321 purple_xfer_conversation_write(xfer, msg, TRUE);
322 purple_xfer_error(xfer_type, account, priv->who, msg);
323 g_free(msg);
326 static void
327 purple_xfer_choose_file_ok_cb(void *user_data, const char *filename)
329 PurpleXfer *xfer;
330 PurpleXferType type;
331 GStatBuf st;
332 gchar *dir;
334 xfer = (PurpleXfer *)user_data;
335 type = purple_xfer_get_xfer_type(xfer);
337 if (g_stat(filename, &st) != 0) {
338 /* File not found. */
339 if (type == PURPLE_XFER_TYPE_RECEIVE) {
340 #ifndef _WIN32
341 int mode = W_OK;
342 #else
343 int mode = F_OK;
344 #endif
345 dir = g_path_get_dirname(filename);
347 if (g_access(dir, mode) == 0) {
348 purple_xfer_request_accepted(xfer, filename);
349 } else {
350 g_object_ref(xfer);
351 purple_notify_message(
352 NULL, PURPLE_NOTIFY_MSG_ERROR, NULL,
353 _("Directory is not writable."), NULL,
354 purple_request_cpar_from_account(
355 purple_xfer_get_account(xfer)),
356 (PurpleNotifyCloseCallback)purple_xfer_choose_file, xfer);
359 g_free(dir);
361 else {
362 purple_xfer_show_file_error(xfer, filename);
363 purple_xfer_cancel_local(xfer);
366 else if ((type == PURPLE_XFER_TYPE_SEND) && (st.st_size == 0)) {
368 purple_notify_error(NULL, NULL,
369 _("Cannot send a file of 0 bytes."), NULL,
370 purple_request_cpar_from_account(
371 purple_xfer_get_account(xfer)));
373 purple_xfer_cancel_local(xfer);
375 else if ((type == PURPLE_XFER_TYPE_SEND) && S_ISDIR(st.st_mode)) {
377 * XXX - Sending a directory should be valid for some protocols.
379 purple_notify_error(NULL, NULL, _("Cannot send a directory."),
380 NULL, purple_request_cpar_from_account(
381 purple_xfer_get_account(xfer)));
383 purple_xfer_cancel_local(xfer);
385 else if ((type == PURPLE_XFER_TYPE_RECEIVE) && S_ISDIR(st.st_mode)) {
386 char *msg, *utf8;
387 utf8 = g_filename_to_utf8(filename, -1, NULL, NULL, NULL);
388 msg = g_strdup_printf(
389 _("%s is not a regular file. Cowardly refusing to overwrite it.\n"), utf8);
390 g_free(utf8);
391 purple_notify_error(NULL, NULL, msg, NULL,
392 purple_request_cpar_from_account(
393 purple_xfer_get_account(xfer)));
394 g_free(msg);
395 purple_xfer_request_denied(xfer);
397 else if (type == PURPLE_XFER_TYPE_SEND) {
398 #ifndef _WIN32
399 int mode = R_OK;
400 #else
401 int mode = F_OK;
402 #endif
404 if (g_access(filename, mode) == 0) {
405 purple_xfer_request_accepted(xfer, filename);
406 } else {
407 g_object_ref(xfer);
408 purple_notify_message(
409 NULL, PURPLE_NOTIFY_MSG_ERROR, NULL,
410 _("File is not readable."), NULL,
411 purple_request_cpar_from_account(
412 purple_xfer_get_account(xfer)),
413 (PurpleNotifyCloseCallback)purple_xfer_choose_file, xfer);
416 else {
417 purple_xfer_request_accepted(xfer, filename);
420 g_object_unref(xfer);
423 static void
424 purple_xfer_choose_file_cancel_cb(void *user_data, const char *filename)
426 PurpleXfer *xfer = (PurpleXfer *)user_data;
428 purple_xfer_set_status(xfer, PURPLE_XFER_STATUS_CANCEL_LOCAL);
429 if (purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_SEND)
430 purple_xfer_cancel_local(xfer);
431 else
432 purple_xfer_request_denied(xfer);
433 g_object_unref(xfer);
436 static int
437 purple_xfer_choose_file(PurpleXfer *xfer)
439 purple_request_file(xfer, NULL, purple_xfer_get_filename(xfer),
440 (purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_RECEIVE),
441 G_CALLBACK(purple_xfer_choose_file_ok_cb),
442 G_CALLBACK(purple_xfer_choose_file_cancel_cb),
443 purple_request_cpar_from_account(purple_xfer_get_account(xfer)),
444 xfer);
446 return 0;
449 static int
450 cancel_recv_cb(PurpleXfer *xfer)
452 purple_xfer_set_status(xfer, PURPLE_XFER_STATUS_CANCEL_LOCAL);
453 purple_xfer_request_denied(xfer);
454 g_object_unref(xfer);
456 return 0;
459 static void
460 purple_xfer_ask_recv(PurpleXfer *xfer)
462 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
463 char *buf, *size_buf;
464 goffset size;
465 gconstpointer thumb;
466 gsize thumb_size;
468 /* If we have already accepted the request, ask the destination file
469 name directly */
470 if (purple_xfer_get_status(xfer) != PURPLE_XFER_STATUS_ACCEPTED) {
471 PurpleRequestCommonParameters *cpar;
472 PurpleBuddy *buddy = purple_blist_find_buddy(priv->account, priv->who);
474 if (purple_xfer_get_filename(xfer) != NULL)
476 size = purple_xfer_get_size(xfer);
477 size_buf = purple_str_size_to_units(size);
478 buf = g_strdup_printf(_("%s wants to send you %s (%s)"),
479 buddy ? purple_buddy_get_alias(buddy) : priv->who,
480 purple_xfer_get_filename(xfer), size_buf);
481 g_free(size_buf);
483 else
485 buf = g_strdup_printf(_("%s wants to send you a file"),
486 buddy ? purple_buddy_get_alias(buddy) : priv->who);
489 if (priv->message != NULL)
490 purple_serv_got_im(purple_account_get_connection(priv->account),
491 priv->who, priv->message, 0, time(NULL));
493 cpar = purple_request_cpar_from_account(priv->account);
494 if ((thumb = purple_xfer_get_thumbnail(xfer, &thumb_size))) {
495 purple_request_cpar_set_custom_icon(cpar, thumb,
496 thumb_size);
499 purple_request_accept_cancel(xfer, NULL, buf, NULL,
500 PURPLE_DEFAULT_ACTION_NONE, cpar, xfer,
501 G_CALLBACK(purple_xfer_choose_file),
502 G_CALLBACK(cancel_recv_cb));
504 g_free(buf);
505 } else
506 purple_xfer_choose_file(xfer);
509 static int
510 ask_accept_ok(PurpleXfer *xfer)
512 purple_xfer_request_accepted(xfer, NULL);
514 return 0;
517 static int
518 ask_accept_cancel(PurpleXfer *xfer)
520 purple_xfer_request_denied(xfer);
521 g_object_unref(xfer);
523 return 0;
526 static void
527 purple_xfer_ask_accept(PurpleXfer *xfer)
529 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
530 char *buf, *buf2 = NULL;
531 PurpleBuddy *buddy = purple_blist_find_buddy(priv->account, priv->who);
533 buf = g_strdup_printf(_("Accept file transfer request from %s?"),
534 buddy ? purple_buddy_get_alias(buddy) : priv->who);
535 if (purple_xfer_get_remote_ip(xfer) &&
536 purple_xfer_get_remote_port(xfer))
537 buf2 = g_strdup_printf(_("A file is available for download from:\n"
538 "Remote host: %s\nRemote port: %d"),
539 purple_xfer_get_remote_ip(xfer),
540 purple_xfer_get_remote_port(xfer));
541 purple_request_accept_cancel(xfer, NULL, buf, buf2,
542 PURPLE_DEFAULT_ACTION_NONE,
543 purple_request_cpar_from_account(priv->account), xfer,
544 G_CALLBACK(ask_accept_ok), G_CALLBACK(ask_accept_cancel));
545 g_free(buf);
546 g_free(buf2);
549 void
550 purple_xfer_request(PurpleXfer *xfer)
552 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
554 g_return_if_fail(priv != NULL);
555 g_return_if_fail(priv->ops.init != NULL);
557 g_object_ref(xfer);
559 if (purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_RECEIVE)
561 purple_signal_emit(purple_xfers_get_handle(), "file-recv-request", xfer);
562 if (purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_CANCEL_LOCAL)
564 /* The file-transfer was cancelled by a plugin */
565 purple_xfer_cancel_local(xfer);
567 else if (purple_xfer_get_filename(xfer) ||
568 purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_ACCEPTED)
570 gchar* message = NULL;
571 PurpleBuddy *buddy = purple_blist_find_buddy(priv->account, priv->who);
573 message = g_strdup_printf(_("%s is offering to send file %s"),
574 buddy ? purple_buddy_get_alias(buddy) : priv->who, purple_xfer_get_filename(xfer));
575 purple_xfer_conversation_write_with_thumbnail(xfer, message);
576 g_free(message);
578 /* Ask for a filename to save to if it's not already given by a plugin */
579 if (priv->local_filename == NULL)
580 purple_xfer_ask_recv(xfer);
582 else
584 purple_xfer_ask_accept(xfer);
587 else
589 purple_xfer_choose_file(xfer);
593 void
594 purple_xfer_request_accepted(PurpleXfer *xfer, const char *filename)
596 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
597 PurpleXferType type;
598 GStatBuf st;
599 char *msg, *utf8, *base;
600 PurpleAccount *account;
601 PurpleBuddy *buddy;
603 if (priv == NULL)
604 return;
606 type = purple_xfer_get_xfer_type(xfer);
607 account = purple_xfer_get_account(xfer);
609 purple_debug_misc("xfer", "request accepted for %p\n", xfer);
611 if (!filename && type == PURPLE_XFER_TYPE_RECEIVE) {
612 purple_xfer_set_status(xfer, PURPLE_XFER_STATUS_ACCEPTED);
613 priv->ops.init(xfer);
614 return;
615 } else {
616 g_return_if_fail(filename != NULL);
619 buddy = purple_blist_find_buddy(account, priv->who);
621 if (type == PURPLE_XFER_TYPE_SEND) {
622 /* Sending a file */
623 /* Check the filename. */
624 PurpleXferUiOps *ui_ops;
625 ui_ops = purple_xfer_get_ui_ops(xfer);
627 #ifdef _WIN32
628 if (g_strrstr(filename, "../") || g_strrstr(filename, "..\\"))
629 #else
630 if (g_strrstr(filename, "../"))
631 #endif
633 utf8 = g_filename_to_utf8(filename, -1, NULL, NULL, NULL);
635 msg = g_strdup_printf(_("%s is not a valid filename.\n"), utf8);
636 purple_xfer_error(type, account, priv->who, msg);
637 g_free(utf8);
638 g_free(msg);
640 g_object_unref(xfer);
641 return;
644 if (ui_ops == NULL || (ui_ops->ui_read == NULL && ui_ops->ui_write == NULL)) {
645 if (g_stat(filename, &st) == -1) {
646 purple_xfer_show_file_error(xfer, filename);
647 g_object_unref(xfer);
648 return;
651 purple_xfer_set_local_filename(xfer, filename);
652 purple_xfer_set_size(xfer, st.st_size);
653 } else {
654 purple_xfer_set_local_filename(xfer, filename);
657 base = g_path_get_basename(filename);
658 utf8 = g_filename_to_utf8(base, -1, NULL, NULL, NULL);
659 g_free(base);
660 purple_xfer_set_filename(xfer, utf8);
662 msg = g_strdup_printf(_("Offering to send %s to %s"),
663 utf8, buddy ? purple_buddy_get_alias(buddy) : priv->who);
664 g_free(utf8);
665 purple_xfer_conversation_write(xfer, msg, FALSE);
666 g_free(msg);
668 else {
669 /* Receiving a file */
670 purple_xfer_set_status(xfer, PURPLE_XFER_STATUS_ACCEPTED);
671 purple_xfer_set_local_filename(xfer, filename);
673 msg = g_strdup_printf(_("Starting transfer of %s from %s"),
674 priv->filename, buddy ? purple_buddy_get_alias(buddy) : priv->who);
675 purple_xfer_conversation_write(xfer, msg, FALSE);
676 g_free(msg);
679 purple_xfer_add(xfer);
680 priv->ops.init(xfer);
684 void
685 purple_xfer_request_denied(PurpleXfer *xfer)
687 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
689 g_return_if_fail(priv != NULL);
691 purple_debug_misc("xfer", "xfer %p denied\n", xfer);
693 if (priv->ops.request_denied != NULL)
694 priv->ops.request_denied(xfer);
696 g_object_unref(xfer);
699 int purple_xfer_get_fd(PurpleXfer *xfer)
701 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
703 g_return_val_if_fail(priv != NULL, 0);
705 return priv->fd;
708 int purple_xfer_get_watcher(PurpleXfer *xfer)
710 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
712 g_return_val_if_fail(priv != NULL, 0);
714 return priv->watcher;
717 PurpleXferType
718 purple_xfer_get_xfer_type(const PurpleXfer *xfer)
720 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
722 g_return_val_if_fail(priv != NULL, PURPLE_XFER_TYPE_UNKNOWN);
724 return priv->type;
727 PurpleAccount *
728 purple_xfer_get_account(const PurpleXfer *xfer)
730 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
732 g_return_val_if_fail(priv != NULL, NULL);
734 return priv->account;
737 void
738 purple_xfer_set_remote_user(PurpleXfer *xfer, const char *who)
740 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
742 g_return_if_fail(priv != NULL);
744 g_free(priv->who);
745 priv->who = g_strdup(who);
747 g_object_notify_by_pspec(G_OBJECT(xfer), properties[PROP_REMOTE_USER]);
750 const char *
751 purple_xfer_get_remote_user(const PurpleXfer *xfer)
753 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
755 g_return_val_if_fail(priv != NULL, NULL);
757 return priv->who;
760 PurpleXferStatus
761 purple_xfer_get_status(const PurpleXfer *xfer)
763 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
765 g_return_val_if_fail(priv != NULL, PURPLE_XFER_STATUS_UNKNOWN);
767 return priv->status;
770 gboolean
771 purple_xfer_is_cancelled(const PurpleXfer *xfer)
773 g_return_val_if_fail(PURPLE_IS_XFER(xfer), TRUE);
775 if ((purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_CANCEL_LOCAL) ||
776 (purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_CANCEL_REMOTE))
777 return TRUE;
778 else
779 return FALSE;
782 gboolean
783 purple_xfer_is_completed(const PurpleXfer *xfer)
785 g_return_val_if_fail(PURPLE_IS_XFER(xfer), TRUE);
787 return (purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_DONE);
790 const char *
791 purple_xfer_get_filename(const PurpleXfer *xfer)
793 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
795 g_return_val_if_fail(priv != NULL, NULL);
797 return priv->filename;
800 const char *
801 purple_xfer_get_local_filename(const PurpleXfer *xfer)
803 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
805 g_return_val_if_fail(priv != NULL, NULL);
807 return priv->local_filename;
810 goffset
811 purple_xfer_get_bytes_sent(const PurpleXfer *xfer)
813 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
815 g_return_val_if_fail(priv != NULL, 0);
817 return priv->bytes_sent;
820 goffset
821 purple_xfer_get_bytes_remaining(const PurpleXfer *xfer)
823 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
825 g_return_val_if_fail(priv != NULL, 0);
827 return priv->size - priv->bytes_sent;
830 goffset
831 purple_xfer_get_size(const PurpleXfer *xfer)
833 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
835 g_return_val_if_fail(priv != NULL, 0);
837 return priv->size;
840 double
841 purple_xfer_get_progress(const PurpleXfer *xfer)
843 g_return_val_if_fail(PURPLE_IS_XFER(xfer), 0.0);
845 if (purple_xfer_get_size(xfer) == 0)
846 return 0.0;
848 return ((double)purple_xfer_get_bytes_sent(xfer) /
849 (double)purple_xfer_get_size(xfer));
852 guint16
853 purple_xfer_get_local_port(const PurpleXfer *xfer)
855 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
857 g_return_val_if_fail(priv != NULL, -1);
859 return priv->local_port;
862 const char *
863 purple_xfer_get_remote_ip(const PurpleXfer *xfer)
865 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
867 g_return_val_if_fail(priv != NULL, NULL);
869 return priv->remote_ip;
872 guint16
873 purple_xfer_get_remote_port(const PurpleXfer *xfer)
875 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
877 g_return_val_if_fail(priv != NULL, -1);
879 return priv->remote_port;
882 time_t
883 purple_xfer_get_start_time(const PurpleXfer *xfer)
885 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
887 g_return_val_if_fail(priv != NULL, 0);
889 return priv->start_time;
892 time_t
893 purple_xfer_get_end_time(const PurpleXfer *xfer)
895 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
897 g_return_val_if_fail(priv != NULL, 0);
899 return priv->end_time;
902 void purple_xfer_set_fd(PurpleXfer *xfer, int fd)
904 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
906 g_return_if_fail(priv != NULL);
908 priv->fd = fd;
910 g_object_notify_by_pspec(G_OBJECT(xfer), properties[PROP_FD]);
913 void purple_xfer_set_watcher(PurpleXfer *xfer, int watcher)
915 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
917 g_return_if_fail(priv != NULL);
919 priv->watcher = watcher;
921 g_object_notify_by_pspec(G_OBJECT(xfer), properties[PROP_WATCHER]);
924 void
925 purple_xfer_set_completed(PurpleXfer *xfer, gboolean completed)
927 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
928 PurpleXferUiOps *ui_ops;
930 g_return_if_fail(priv != NULL);
932 if (completed == TRUE) {
933 char *msg = NULL;
934 PurpleIMConversation *im;
936 purple_xfer_set_status(xfer, PURPLE_XFER_STATUS_DONE);
938 if (purple_xfer_get_filename(xfer) != NULL)
940 char *filename = g_markup_escape_text(purple_xfer_get_filename(xfer), -1);
941 if (purple_xfer_get_local_filename(xfer)
942 && purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_RECEIVE)
944 char *local = g_markup_escape_text(purple_xfer_get_local_filename(xfer), -1);
945 msg = g_strdup_printf(_("Transfer of file <A HREF=\"file://%s\">%s</A> complete"),
946 local, filename);
947 g_free(local);
949 else
950 msg = g_strdup_printf(_("Transfer of file %s complete"),
951 filename);
952 g_free(filename);
954 else
955 msg = g_strdup(_("File transfer complete"));
957 im = purple_conversations_find_im_with_account(priv->who,
958 purple_xfer_get_account(xfer));
960 if (im != NULL) {
961 purple_conversation_write_system_message(
962 PURPLE_CONVERSATION(im), msg, 0);
964 g_free(msg);
967 ui_ops = purple_xfer_get_ui_ops(xfer);
969 if (ui_ops != NULL && ui_ops->update_progress != NULL)
970 ui_ops->update_progress(xfer, purple_xfer_get_progress(xfer));
973 void
974 purple_xfer_set_message(PurpleXfer *xfer, const char *message)
976 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
978 g_return_if_fail(priv != NULL);
980 g_free(priv->message);
981 priv->message = g_strdup(message);
983 g_object_notify_by_pspec(G_OBJECT(xfer), properties[PROP_MESSAGE]);
986 const char *
987 purple_xfer_get_message(const PurpleXfer *xfer)
989 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
991 g_return_val_if_fail(priv != NULL, NULL);
993 return priv->message;
996 void
997 purple_xfer_set_filename(PurpleXfer *xfer, const char *filename)
999 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
1001 g_return_if_fail(priv != NULL);
1003 g_free(priv->filename);
1004 priv->filename = g_strdup(filename);
1006 g_object_notify_by_pspec(G_OBJECT(xfer), properties[PROP_FILENAME]);
1009 void
1010 purple_xfer_set_local_filename(PurpleXfer *xfer, const char *filename)
1012 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
1014 g_return_if_fail(priv != NULL);
1016 g_free(priv->local_filename);
1017 priv->local_filename = g_strdup(filename);
1019 g_object_notify_by_pspec(G_OBJECT(xfer), properties[PROP_LOCAL_FILENAME]);
1022 void
1023 purple_xfer_set_size(PurpleXfer *xfer, goffset size)
1025 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
1027 g_return_if_fail(priv != NULL);
1029 priv->size = size;
1031 g_object_notify_by_pspec(G_OBJECT(xfer), properties[PROP_FILE_SIZE]);
1034 void
1035 purple_xfer_set_local_port(PurpleXfer *xfer, guint16 local_port)
1037 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
1039 g_return_if_fail(priv != NULL);
1041 priv->local_port = local_port;
1043 g_object_notify_by_pspec(G_OBJECT(xfer), properties[PROP_LOCAL_PORT]);
1046 void
1047 purple_xfer_set_bytes_sent(PurpleXfer *xfer, goffset bytes_sent)
1049 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
1051 g_return_if_fail(priv != NULL);
1053 priv->bytes_sent = bytes_sent;
1055 g_object_notify_by_pspec(G_OBJECT(xfer), properties[PROP_BYTES_SENT]);
1058 PurpleXferUiOps *
1059 purple_xfer_get_ui_ops(const PurpleXfer *xfer)
1061 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
1063 g_return_val_if_fail(priv != NULL, NULL);
1065 return priv->ui_ops;
1068 void
1069 purple_xfer_set_init_fnc(PurpleXfer *xfer, void (*fnc)(PurpleXfer *))
1071 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
1073 g_return_if_fail(priv != NULL);
1075 priv->ops.init = fnc;
1078 void purple_xfer_set_request_denied_fnc(PurpleXfer *xfer, void (*fnc)(PurpleXfer *))
1080 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
1082 g_return_if_fail(priv != NULL);
1084 priv->ops.request_denied = fnc;
1087 void
1088 purple_xfer_set_read_fnc(PurpleXfer *xfer, gssize (*fnc)(guchar **, size_t, PurpleXfer *))
1090 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
1092 g_return_if_fail(priv != NULL);
1094 priv->ops.read = fnc;
1097 void
1098 purple_xfer_set_write_fnc(PurpleXfer *xfer,
1099 gssize (*fnc)(const guchar *, size_t, PurpleXfer *))
1101 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
1103 g_return_if_fail(priv != NULL);
1105 priv->ops.write = fnc;
1108 void
1109 purple_xfer_set_ack_fnc(PurpleXfer *xfer,
1110 void (*fnc)(PurpleXfer *, const guchar *, size_t))
1112 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
1114 g_return_if_fail(priv != NULL);
1116 priv->ops.ack = fnc;
1119 void
1120 purple_xfer_set_start_fnc(PurpleXfer *xfer, void (*fnc)(PurpleXfer *))
1122 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
1124 g_return_if_fail(priv != NULL);
1126 priv->ops.start = fnc;
1129 void
1130 purple_xfer_set_end_fnc(PurpleXfer *xfer, void (*fnc)(PurpleXfer *))
1132 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
1134 g_return_if_fail(priv != NULL);
1136 priv->ops.end = fnc;
1139 void
1140 purple_xfer_set_cancel_send_fnc(PurpleXfer *xfer, void (*fnc)(PurpleXfer *))
1142 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
1144 g_return_if_fail(priv != NULL);
1146 priv->ops.cancel_send = fnc;
1149 void
1150 purple_xfer_set_cancel_recv_fnc(PurpleXfer *xfer, void (*fnc)(PurpleXfer *))
1152 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
1154 g_return_if_fail(priv != NULL);
1156 priv->ops.cancel_recv = fnc;
1159 static void
1160 purple_xfer_increase_buffer_size(PurpleXfer *xfer)
1162 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
1164 priv->current_buffer_size = MIN(priv->current_buffer_size * 1.5,
1165 FT_MAX_BUFFER_SIZE);
1168 gssize
1169 purple_xfer_read(PurpleXfer *xfer, guchar **buffer)
1171 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
1172 gssize s, r;
1174 g_return_val_if_fail(priv != NULL, 0);
1175 g_return_val_if_fail(buffer != NULL, 0);
1177 if (purple_xfer_get_size(xfer) == 0)
1178 s = priv->current_buffer_size;
1179 else
1180 s = MIN((gssize)purple_xfer_get_bytes_remaining(xfer), (gssize)priv->current_buffer_size);
1182 if (priv->ops.read != NULL) {
1183 r = (priv->ops.read)(buffer, s, xfer);
1185 else {
1186 *buffer = g_malloc0(s);
1188 r = read(priv->fd, *buffer, s);
1189 if (r < 0 && errno == EAGAIN)
1190 r = 0;
1191 else if (r < 0)
1192 r = -1;
1193 else if (r == 0)
1194 r = -1;
1197 if (r >= 0 && (gsize)r == priv->current_buffer_size)
1199 * We managed to read the entire buffer. This means our this
1200 * network is fast and our buffer is too small, so make it
1201 * bigger.
1203 purple_xfer_increase_buffer_size(xfer);
1205 return r;
1208 static gssize
1209 do_write(PurpleXfer *xfer, const guchar *buffer, gsize size)
1211 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
1212 gssize r;
1214 g_return_val_if_fail(priv != NULL, 0);
1215 g_return_val_if_fail(buffer != NULL, 0);
1216 g_return_val_if_fail(size != 0, 0);
1218 if (priv->ops.write != NULL) {
1219 r = (priv->ops.write)(buffer, size, xfer);
1220 } else {
1221 r = write(priv->fd, buffer, size);
1222 if (r < 0 && errno == EAGAIN)
1223 r = 0;
1226 return r;
1229 gssize
1230 purple_xfer_write(PurpleXfer *xfer, const guchar *buffer, gsize size)
1232 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
1233 gssize s;
1235 g_return_val_if_fail(priv != NULL, 0);
1237 s = MIN((gssize)purple_xfer_get_bytes_remaining(xfer), (gssize)size);
1239 return do_write(xfer, buffer, s);
1242 gboolean
1243 purple_xfer_write_file(PurpleXfer *xfer, const guchar *buffer, gsize size)
1245 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
1246 PurpleXferUiOps *ui_ops;
1247 gsize wc;
1248 gboolean fs_known;
1250 g_return_val_if_fail(priv != NULL, FALSE);
1251 g_return_val_if_fail(buffer != NULL, FALSE);
1253 ui_ops = purple_xfer_get_ui_ops(xfer);
1254 fs_known = (purple_xfer_get_size(xfer) > 0);
1256 if (fs_known && (goffset)size > purple_xfer_get_bytes_remaining(xfer)) {
1257 purple_debug_warning("xfer",
1258 "Got too much data (truncating at %" G_GOFFSET_FORMAT
1259 ").\n", purple_xfer_get_size(xfer));
1260 size = purple_xfer_get_bytes_remaining(xfer);
1263 if (ui_ops && ui_ops->ui_write)
1264 wc = ui_ops->ui_write(xfer, buffer, size);
1265 else {
1266 if (priv->dest_fp == NULL) {
1267 purple_debug_error("xfer",
1268 "File is not opened for writing\n");
1269 purple_xfer_cancel_local(xfer);
1270 return FALSE;
1272 wc = fwrite(buffer, 1, size, priv->dest_fp);
1275 if (wc != size) {
1276 purple_debug_error("xfer",
1277 "Unable to write whole buffer.\n");
1278 purple_xfer_cancel_local(xfer);
1279 return FALSE;
1282 purple_xfer_set_bytes_sent(xfer, purple_xfer_get_bytes_sent(xfer) +
1283 size);
1285 return TRUE;
1288 gssize
1289 purple_xfer_read_file(PurpleXfer *xfer, guchar *buffer, gsize size)
1291 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
1292 PurpleXferUiOps *ui_ops;
1293 gssize got_len;
1295 g_return_val_if_fail(priv != NULL, FALSE);
1296 g_return_val_if_fail(buffer != NULL, FALSE);
1298 ui_ops = purple_xfer_get_ui_ops(xfer);
1300 if (ui_ops && ui_ops->ui_read) {
1301 guchar *buffer_got = NULL;
1303 got_len = ui_ops->ui_read(xfer, &buffer_got, size);
1305 if (got_len >= 0 && (gsize)got_len > size) {
1306 g_free(buffer_got);
1307 purple_debug_error("xfer",
1308 "Got too much data from UI.\n");
1309 purple_xfer_cancel_local(xfer);
1310 return -1;
1313 if (got_len > 0)
1314 memcpy(buffer, buffer_got, got_len);
1315 g_free(buffer_got);
1316 } else {
1317 if (priv->dest_fp == NULL) {
1318 purple_debug_error("xfer",
1319 "File is not opened for reading\n");
1320 purple_xfer_cancel_local(xfer);
1321 return -1;
1323 got_len = fread(buffer, 1, size, priv->dest_fp);
1324 if ((got_len < 0 || (gsize)got_len != size) &&
1325 ferror(priv->dest_fp))
1327 purple_debug_error("xfer",
1328 "Unable to read file.\n");
1329 purple_xfer_cancel_local(xfer);
1330 return -1;
1334 if (got_len > 0) {
1335 purple_xfer_set_bytes_sent(xfer,
1336 purple_xfer_get_bytes_sent(xfer) + got_len);
1339 return got_len;
1342 static void
1343 do_transfer(PurpleXfer *xfer)
1345 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
1346 PurpleXferUiOps *ui_ops;
1347 guchar *buffer = NULL;
1348 gssize r = 0;
1350 ui_ops = purple_xfer_get_ui_ops(xfer);
1352 if (priv->type == PURPLE_XFER_TYPE_RECEIVE) {
1353 r = purple_xfer_read(xfer, &buffer);
1354 if (r > 0) {
1355 if (!purple_xfer_write_file(xfer, buffer, r)) {
1356 g_free(buffer);
1357 return;
1360 } else if(r < 0) {
1361 purple_xfer_cancel_remote(xfer);
1362 g_free(buffer);
1363 return;
1365 } else if (priv->type == PURPLE_XFER_TYPE_SEND) {
1366 gssize result = 0;
1367 gsize s = MIN((gsize)purple_xfer_get_bytes_remaining(xfer), (gsize)priv->current_buffer_size);
1368 gboolean read = TRUE;
1370 /* this is so the protocol can keep the connection open
1371 if it needs to for some odd reason. */
1372 if (s == 0) {
1373 if (priv->watcher) {
1374 purple_input_remove(priv->watcher);
1375 purple_xfer_set_watcher(xfer, 0);
1377 return;
1380 if (priv->buffer) {
1381 if (priv->buffer->len < s) {
1382 s -= priv->buffer->len;
1383 read = TRUE;
1384 } else {
1385 read = FALSE;
1389 if (read) {
1390 buffer = g_new(guchar, s);
1391 result = purple_xfer_read_file(xfer, buffer, s);
1392 if (result == 0) {
1394 * The UI claimed it was ready, but didn't have any data for
1395 * us... It will call purple_xfer_ui_ready when ready, which
1396 * sets back up this watcher.
1398 if (priv->watcher != 0) {
1399 purple_input_remove(priv->watcher);
1400 purple_xfer_set_watcher(xfer, 0);
1403 /* Need to indicate the protocol is still ready... */
1404 priv->ready |= PURPLE_XFER_READY_PROTOCOL;
1406 g_return_if_reached();
1408 if (result < 0)
1409 return;
1412 if (priv->buffer) {
1413 g_byte_array_append(priv->buffer, buffer, result);
1414 g_free(buffer);
1415 buffer = priv->buffer->data;
1416 result = priv->buffer->len;
1419 r = do_write(xfer, buffer, result);
1421 if (r == -1) {
1422 purple_xfer_cancel_remote(xfer);
1423 if (!priv->buffer)
1424 /* We don't free buffer if priv->buffer is set, because in
1425 that case buffer doesn't belong to us. */
1426 g_free(buffer);
1427 return;
1428 } else if (r == result) {
1430 * We managed to write the entire buffer. This means our
1431 * network is fast and our buffer is too small, so make it
1432 * bigger.
1434 purple_xfer_increase_buffer_size(xfer);
1435 } else {
1436 if (ui_ops && ui_ops->data_not_sent)
1437 ui_ops->data_not_sent(xfer, buffer + r, result - r);
1440 if (priv->buffer) {
1442 * Remove what we wrote
1443 * If we wrote the whole buffer the byte array will be empty
1444 * Otherwise we'll keep what wasn't sent for next time.
1446 buffer = NULL;
1447 g_byte_array_remove_range(priv->buffer, 0, r);
1451 if (r > 0) {
1452 if (priv->ops.ack != NULL)
1453 priv->ops.ack(xfer, buffer, r);
1455 g_free(buffer);
1457 if (ui_ops != NULL && ui_ops->update_progress != NULL)
1458 ui_ops->update_progress(xfer,
1459 purple_xfer_get_progress(xfer));
1462 if (purple_xfer_get_bytes_sent(xfer) >= purple_xfer_get_size(xfer) &&
1463 !purple_xfer_is_completed(xfer)) {
1464 purple_xfer_set_completed(xfer, TRUE);
1467 /* TODO: Check if above is the only place xfers are marked completed.
1468 * If so, merge these conditions.
1470 if (purple_xfer_is_completed(xfer)) {
1471 purple_xfer_end(xfer);
1475 static void
1476 transfer_cb(gpointer data, gint source, PurpleInputCondition condition)
1478 PurpleXfer *xfer = data;
1479 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
1481 if (priv->dest_fp == NULL) {
1482 /* The UI is moderating its side manually */
1483 if (0 == (priv->ready & PURPLE_XFER_READY_UI)) {
1484 priv->ready |= PURPLE_XFER_READY_PROTOCOL;
1486 purple_input_remove(priv->watcher);
1487 purple_xfer_set_watcher(xfer, 0);
1489 purple_debug_misc("xfer", "Protocol is ready on ft %p, waiting for UI\n", xfer);
1490 return;
1493 priv->ready = PURPLE_XFER_READY_NONE;
1496 do_transfer(xfer);
1499 static void
1500 begin_transfer(PurpleXfer *xfer, PurpleInputCondition cond)
1502 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
1503 PurpleXferType type = purple_xfer_get_xfer_type(xfer);
1504 PurpleXferUiOps *ui_ops = purple_xfer_get_ui_ops(xfer);
1506 if (priv->start_time != 0) {
1507 purple_debug_error("xfer", "Transfer is being started multiple times\n");
1508 g_return_if_reached();
1511 if (ui_ops == NULL || (ui_ops->ui_read == NULL && ui_ops->ui_write == NULL)) {
1512 priv->dest_fp = g_fopen(purple_xfer_get_local_filename(xfer),
1513 type == PURPLE_XFER_TYPE_RECEIVE ? "wb" : "rb");
1515 if (priv->dest_fp == NULL) {
1516 purple_xfer_show_file_error(xfer, purple_xfer_get_local_filename(xfer));
1517 purple_xfer_cancel_local(xfer);
1518 return;
1521 if (fseek(priv->dest_fp, priv->bytes_sent, SEEK_SET) != 0) {
1522 purple_debug_error("xfer", "couldn't seek\n");
1523 purple_xfer_show_file_error(xfer, purple_xfer_get_local_filename(xfer));
1524 purple_xfer_cancel_local(xfer);
1525 return;
1529 if (priv->fd != -1)
1530 purple_xfer_set_watcher(xfer,
1531 purple_input_add(priv->fd, cond, transfer_cb, xfer));
1533 priv->start_time = time(NULL);
1535 g_object_notify_by_pspec(G_OBJECT(xfer), properties[PROP_START_TIME]);
1537 if (priv->ops.start != NULL)
1538 priv->ops.start(xfer);
1541 static void
1542 connect_cb(gpointer data, gint source, const gchar *error_message)
1544 PurpleXfer *xfer = PURPLE_XFER(data);
1546 if (source < 0) {
1547 purple_xfer_cancel_local(xfer);
1548 return;
1551 purple_xfer_set_fd(xfer, source);
1552 begin_transfer(xfer, PURPLE_INPUT_READ);
1555 void
1556 purple_xfer_ui_ready(PurpleXfer *xfer)
1558 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
1559 PurpleInputCondition cond;
1560 PurpleXferType type;
1562 g_return_if_fail(priv != NULL);
1564 priv->ready |= PURPLE_XFER_READY_UI;
1566 if (0 == (priv->ready & PURPLE_XFER_READY_PROTOCOL)) {
1567 purple_debug_misc("xfer", "UI is ready on ft %p, waiting for protocol\n", xfer);
1568 return;
1571 purple_debug_misc("xfer", "UI (and protocol) ready on ft %p, so proceeding\n", xfer);
1573 type = purple_xfer_get_xfer_type(xfer);
1574 if (type == PURPLE_XFER_TYPE_SEND)
1575 cond = PURPLE_INPUT_WRITE;
1576 else /* if (type == PURPLE_XFER_TYPE_RECEIVE) */
1577 cond = PURPLE_INPUT_READ;
1579 if (priv->watcher == 0 && priv->fd != -1)
1580 purple_xfer_set_watcher(xfer,
1581 purple_input_add(priv->fd, cond, transfer_cb, xfer));
1583 priv->ready = PURPLE_XFER_READY_NONE;
1585 do_transfer(xfer);
1588 void
1589 purple_xfer_protocol_ready(PurpleXfer *xfer)
1591 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
1593 g_return_if_fail(priv != NULL);
1595 priv->ready |= PURPLE_XFER_READY_PROTOCOL;
1597 /* I don't think fwrite/fread are ever *not* ready */
1598 if (priv->dest_fp == NULL && 0 == (priv->ready & PURPLE_XFER_READY_UI)) {
1599 purple_debug_misc("xfer", "Protocol is ready on ft %p, waiting for UI\n", xfer);
1600 return;
1603 purple_debug_misc("xfer", "Protocol (and UI) ready on ft %p, so proceeding\n", xfer);
1605 priv->ready = PURPLE_XFER_READY_NONE;
1607 do_transfer(xfer);
1610 void
1611 purple_xfer_start(PurpleXfer *xfer, int fd, const char *ip, guint16 port)
1613 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
1614 PurpleInputCondition cond;
1615 PurpleXferType type;
1616 GObject *obj;
1618 g_return_if_fail(priv != NULL);
1619 g_return_if_fail(purple_xfer_get_xfer_type(xfer) != PURPLE_XFER_TYPE_UNKNOWN);
1621 type = purple_xfer_get_xfer_type(xfer);
1623 purple_xfer_set_status(xfer, PURPLE_XFER_STATUS_STARTED);
1625 if (type == PURPLE_XFER_TYPE_RECEIVE) {
1626 cond = PURPLE_INPUT_READ;
1628 if (ip != NULL) {
1629 priv->remote_ip = g_strdup(ip);
1630 priv->remote_port = port;
1632 obj = G_OBJECT(xfer);
1633 g_object_freeze_notify(obj);
1634 g_object_notify_by_pspec(obj, properties[PROP_REMOTE_IP]);
1635 g_object_notify_by_pspec(obj, properties[PROP_REMOTE_PORT]);
1636 g_object_thaw_notify(obj);
1638 /* Establish a file descriptor. */
1639 purple_proxy_connect(NULL, priv->account, priv->remote_ip,
1640 priv->remote_port, connect_cb, xfer);
1642 return;
1644 else {
1645 purple_xfer_set_fd(xfer, fd);
1648 else {
1649 cond = PURPLE_INPUT_WRITE;
1651 purple_xfer_set_fd(xfer, fd);
1654 begin_transfer(xfer, cond);
1657 void
1658 purple_xfer_end(PurpleXfer *xfer)
1660 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
1662 g_return_if_fail(priv != NULL);
1664 /* See if we are actually trying to cancel this. */
1665 if (!purple_xfer_is_completed(xfer)) {
1666 purple_xfer_cancel_local(xfer);
1667 return;
1670 priv->end_time = time(NULL);
1672 g_object_notify_by_pspec(G_OBJECT(xfer), properties[PROP_END_TIME]);
1674 if (priv->ops.end != NULL)
1675 priv->ops.end(xfer);
1677 if (priv->watcher != 0) {
1678 purple_input_remove(priv->watcher);
1679 purple_xfer_set_watcher(xfer, 0);
1682 if (priv->fd != -1) {
1683 if (close(priv->fd)) {
1684 purple_debug_error("xfer", "closing file descr in purple_xfer_end() failed: %s",
1685 g_strerror(errno));
1689 if (priv->dest_fp != NULL) {
1690 if (fclose(priv->dest_fp)) {
1691 purple_debug_error("xfer", "closing dest file in purple_xfer_end() failed: %s",
1692 g_strerror(errno));
1694 priv->dest_fp = NULL;
1697 g_object_unref(xfer);
1700 void
1701 purple_xfer_add(PurpleXfer *xfer)
1703 PurpleXferUiOps *ui_ops;
1705 g_return_if_fail(PURPLE_IS_XFER(xfer));
1707 ui_ops = purple_xfer_get_ui_ops(xfer);
1709 if (ui_ops != NULL && ui_ops->add_xfer != NULL)
1710 ui_ops->add_xfer(xfer);
1713 void
1714 purple_xfer_cancel_local(PurpleXfer *xfer)
1716 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
1717 PurpleXferUiOps *ui_ops;
1718 char *msg = NULL;
1720 g_return_if_fail(priv != NULL);
1722 /* TODO: We definitely want to close any open request dialogs associated
1723 with this transfer. However, in some cases the request dialog might
1724 own a reference on the xfer. This happens at least with the "%s wants
1725 to send you %s" dialog from purple_xfer_ask_recv(). In these cases
1726 the ref count will not be decremented when the request dialog is
1727 closed, so the ref count will never reach 0 and the xfer will never
1728 be freed. This is a memleak and should be fixed. It's not clear what
1729 the correct fix is. Probably requests should have a destroy function
1730 that is called when the request is destroyed. But also, ref counting
1731 xfer objects makes this code REALLY complicated. An alternate fix is
1732 to not ref count and instead just make sure the object still exists
1733 when we try to use it. */
1734 purple_request_close_with_handle(xfer);
1736 purple_xfer_set_status(xfer, PURPLE_XFER_STATUS_CANCEL_LOCAL);
1737 priv->end_time = time(NULL);
1739 g_object_notify_by_pspec(G_OBJECT(xfer), properties[PROP_END_TIME]);
1741 if (purple_xfer_get_filename(xfer) != NULL)
1743 msg = g_strdup_printf(_("You cancelled the transfer of %s"),
1744 purple_xfer_get_filename(xfer));
1746 else
1748 msg = g_strdup(_("File transfer cancelled"));
1750 purple_xfer_conversation_write(xfer, msg, FALSE);
1751 g_free(msg);
1753 if (purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_SEND)
1755 if (priv->ops.cancel_send != NULL)
1756 priv->ops.cancel_send(xfer);
1758 else
1760 if (priv->ops.cancel_recv != NULL)
1761 priv->ops.cancel_recv(xfer);
1764 if (priv->watcher != 0) {
1765 purple_input_remove(priv->watcher);
1766 purple_xfer_set_watcher(xfer, 0);
1769 if (priv->fd != -1)
1770 close(priv->fd);
1772 if (priv->dest_fp != NULL) {
1773 fclose(priv->dest_fp);
1774 priv->dest_fp = NULL;
1777 ui_ops = purple_xfer_get_ui_ops(xfer);
1779 if (ui_ops != NULL && ui_ops->cancel_local != NULL)
1780 ui_ops->cancel_local(xfer);
1782 g_object_unref(xfer);
1785 void
1786 purple_xfer_cancel_remote(PurpleXfer *xfer)
1788 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
1789 PurpleXferUiOps *ui_ops;
1790 gchar *msg;
1791 PurpleAccount *account;
1792 PurpleBuddy *buddy;
1794 g_return_if_fail(priv != NULL);
1796 purple_request_close_with_handle(xfer);
1797 purple_xfer_set_status(xfer, PURPLE_XFER_STATUS_CANCEL_REMOTE);
1798 priv->end_time = time(NULL);
1800 g_object_notify_by_pspec(G_OBJECT(xfer), properties[PROP_END_TIME]);
1802 account = purple_xfer_get_account(xfer);
1803 buddy = purple_blist_find_buddy(account, priv->who);
1805 if (purple_xfer_get_filename(xfer) != NULL)
1807 msg = g_strdup_printf(_("%s cancelled the transfer of %s"),
1808 buddy ? purple_buddy_get_alias(buddy) : priv->who, purple_xfer_get_filename(xfer));
1810 else
1812 msg = g_strdup_printf(_("%s cancelled the file transfer"),
1813 buddy ? purple_buddy_get_alias(buddy) : priv->who);
1815 purple_xfer_conversation_write(xfer, msg, TRUE);
1816 purple_xfer_error(purple_xfer_get_xfer_type(xfer), account, priv->who, msg);
1817 g_free(msg);
1819 if (purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_SEND)
1821 if (priv->ops.cancel_send != NULL)
1822 priv->ops.cancel_send(xfer);
1824 else
1826 if (priv->ops.cancel_recv != NULL)
1827 priv->ops.cancel_recv(xfer);
1830 if (priv->watcher != 0) {
1831 purple_input_remove(priv->watcher);
1832 purple_xfer_set_watcher(xfer, 0);
1835 if (priv->fd != -1)
1836 close(priv->fd);
1838 if (priv->dest_fp != NULL) {
1839 fclose(priv->dest_fp);
1840 priv->dest_fp = NULL;
1843 ui_ops = purple_xfer_get_ui_ops(xfer);
1845 if (ui_ops != NULL && ui_ops->cancel_remote != NULL)
1846 ui_ops->cancel_remote(xfer);
1848 g_object_unref(xfer);
1851 void
1852 purple_xfer_error(PurpleXferType type, PurpleAccount *account, const char *who, const char *msg)
1854 char *title;
1856 g_return_if_fail(msg != NULL);
1857 g_return_if_fail(type != PURPLE_XFER_TYPE_UNKNOWN);
1859 if (account) {
1860 PurpleBuddy *buddy;
1861 buddy = purple_blist_find_buddy(account, who);
1862 if (buddy)
1863 who = purple_buddy_get_alias(buddy);
1866 if (type == PURPLE_XFER_TYPE_SEND)
1867 title = g_strdup_printf(_("File transfer to %s failed."), who);
1868 else
1869 title = g_strdup_printf(_("File transfer from %s failed."), who);
1871 purple_notify_error(NULL, NULL, title, msg,
1872 purple_request_cpar_from_account(account));
1874 g_free(title);
1877 void
1878 purple_xfer_update_progress(PurpleXfer *xfer)
1880 PurpleXferUiOps *ui_ops;
1882 g_return_if_fail(PURPLE_IS_XFER(xfer));
1884 ui_ops = purple_xfer_get_ui_ops(xfer);
1885 if (ui_ops != NULL && ui_ops->update_progress != NULL)
1886 ui_ops->update_progress(xfer, purple_xfer_get_progress(xfer));
1889 gconstpointer
1890 purple_xfer_get_thumbnail(const PurpleXfer *xfer, gsize *len)
1892 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
1894 g_return_val_if_fail(priv != NULL, NULL);
1896 if (len)
1897 *len = priv->thumbnail_size;
1899 return priv->thumbnail_data;
1902 const gchar *
1903 purple_xfer_get_thumbnail_mimetype(const PurpleXfer *xfer)
1905 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
1907 g_return_val_if_fail(priv != NULL, NULL);
1909 return priv->thumbnail_mimetype;
1912 void
1913 purple_xfer_set_thumbnail(PurpleXfer *xfer, gconstpointer thumbnail,
1914 gsize size, const gchar *mimetype)
1916 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
1918 g_return_if_fail(priv != NULL);
1920 g_free(priv->thumbnail_data);
1921 g_free(priv->thumbnail_mimetype);
1923 if (thumbnail && size > 0) {
1924 priv->thumbnail_data = g_memdup(thumbnail, size);
1925 priv->thumbnail_size = size;
1926 priv->thumbnail_mimetype = g_strdup(mimetype);
1927 } else {
1928 priv->thumbnail_data = NULL;
1929 priv->thumbnail_size = 0;
1930 priv->thumbnail_mimetype = NULL;
1934 void
1935 purple_xfer_prepare_thumbnail(PurpleXfer *xfer, const gchar *formats)
1937 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
1939 g_return_if_fail(priv != NULL);
1941 if (priv->ui_ops->add_thumbnail) {
1942 priv->ui_ops->add_thumbnail(xfer, formats);
1946 void
1947 purple_xfer_set_protocol_data(PurpleXfer *xfer, gpointer proto_data)
1949 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
1951 g_return_if_fail(priv != NULL);
1953 priv->proto_data = proto_data;
1956 gpointer
1957 purple_xfer_get_protocol_data(const PurpleXfer *xfer)
1959 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
1961 g_return_val_if_fail(priv != NULL, NULL);
1963 return priv->proto_data;
1966 void purple_xfer_set_ui_data(PurpleXfer *xfer, gpointer ui_data)
1968 g_return_if_fail(PURPLE_IS_XFER(xfer));
1970 xfer->ui_data = ui_data;
1973 gpointer purple_xfer_get_ui_data(const PurpleXfer *xfer)
1975 g_return_val_if_fail(PURPLE_IS_XFER(xfer), NULL);
1977 return xfer->ui_data;
1980 /**************************************************************************
1981 * GObject code
1982 **************************************************************************/
1983 /* Set method for GObject properties */
1984 static void
1985 purple_xfer_set_property(GObject *obj, guint param_id, const GValue *value,
1986 GParamSpec *pspec)
1988 PurpleXfer *xfer = PURPLE_XFER(obj);
1989 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
1991 switch (param_id) {
1992 case PROP_TYPE:
1993 priv->type = g_value_get_enum(value);
1994 break;
1995 case PROP_ACCOUNT:
1996 priv->account = g_value_get_object(value);
1997 break;
1998 case PROP_REMOTE_USER:
1999 purple_xfer_set_remote_user(xfer, g_value_get_string(value));
2000 break;
2001 case PROP_MESSAGE:
2002 purple_xfer_set_message(xfer, g_value_get_string(value));
2003 break;
2004 case PROP_FILENAME:
2005 purple_xfer_set_filename(xfer, g_value_get_string(value));
2006 break;
2007 case PROP_LOCAL_FILENAME:
2008 purple_xfer_set_local_filename(xfer, g_value_get_string(value));
2009 break;
2010 case PROP_FILE_SIZE:
2011 purple_xfer_set_size(xfer, g_value_get_int64(value));
2012 break;
2013 case PROP_LOCAL_PORT:
2014 purple_xfer_set_local_port(xfer, g_value_get_int(value));
2015 break;
2016 case PROP_FD:
2017 purple_xfer_set_fd(xfer, g_value_get_int(value));
2018 break;
2019 case PROP_WATCHER:
2020 purple_xfer_set_watcher(xfer, g_value_get_int(value));
2021 break;
2022 case PROP_BYTES_SENT:
2023 purple_xfer_set_bytes_sent(xfer, g_value_get_int64(value));
2024 break;
2025 case PROP_STATUS:
2026 purple_xfer_set_status(xfer, g_value_get_enum(value));
2027 break;
2028 default:
2029 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
2030 break;
2034 /* Get method for GObject properties */
2035 static void
2036 purple_xfer_get_property(GObject *obj, guint param_id, GValue *value,
2037 GParamSpec *pspec)
2039 PurpleXfer *xfer = PURPLE_XFER(obj);
2041 switch (param_id) {
2042 case PROP_TYPE:
2043 g_value_set_enum(value, purple_xfer_get_xfer_type(xfer));
2044 break;
2045 case PROP_ACCOUNT:
2046 g_value_set_object(value, purple_xfer_get_account(xfer));
2047 break;
2048 case PROP_REMOTE_USER:
2049 g_value_set_string(value, purple_xfer_get_remote_user(xfer));
2050 break;
2051 case PROP_MESSAGE:
2052 g_value_set_string(value, purple_xfer_get_message(xfer));
2053 break;
2054 case PROP_FILENAME:
2055 g_value_set_string(value, purple_xfer_get_filename(xfer));
2056 break;
2057 case PROP_LOCAL_FILENAME:
2058 g_value_set_string(value, purple_xfer_get_local_filename(xfer));
2059 break;
2060 case PROP_FILE_SIZE:
2061 g_value_set_int64(value, purple_xfer_get_size(xfer));
2062 break;
2063 case PROP_REMOTE_IP:
2064 g_value_set_string(value, purple_xfer_get_remote_ip(xfer));
2065 break;
2066 case PROP_LOCAL_PORT:
2067 g_value_set_int(value, purple_xfer_get_local_port(xfer));
2068 break;
2069 case PROP_REMOTE_PORT:
2070 g_value_set_int(value, purple_xfer_get_remote_port(xfer));
2071 break;
2072 case PROP_FD:
2073 g_value_set_int(value, purple_xfer_get_fd(xfer));
2074 break;
2075 case PROP_WATCHER:
2076 g_value_set_int(value, purple_xfer_get_watcher(xfer));
2077 break;
2078 case PROP_BYTES_SENT:
2079 g_value_set_int64(value, purple_xfer_get_bytes_sent(xfer));
2080 break;
2081 case PROP_START_TIME:
2082 #if SIZEOF_TIME_T == 4
2083 g_value_set_int(value, purple_xfer_get_start_time(xfer));
2084 #elif SIZEOF_TIME_T == 8
2085 g_value_set_int64(value, purple_xfer_get_start_time(xfer));
2086 #else
2087 #error Unknown size of time_t
2088 #endif
2089 break;
2090 case PROP_END_TIME:
2091 #if SIZEOF_TIME_T == 4
2092 g_value_set_int(value, purple_xfer_get_end_time(xfer));
2093 #elif SIZEOF_TIME_T == 8
2094 g_value_set_int64(value, purple_xfer_get_end_time(xfer));
2095 #else
2096 #error Unknown size of time_t
2097 #endif
2098 break;
2099 case PROP_STATUS:
2100 g_value_set_enum(value, purple_xfer_get_status(xfer));
2101 break;
2102 default:
2103 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
2104 break;
2108 /* GObject initialization function */
2109 static void
2110 purple_xfer_init(GTypeInstance *instance, gpointer klass)
2112 PurpleXfer *xfer = PURPLE_XFER(instance);
2113 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
2115 PURPLE_DBUS_REGISTER_POINTER(xfer, PurpleXfer);
2117 priv->ui_ops = purple_xfers_get_ui_ops();
2118 priv->current_buffer_size = FT_INITIAL_BUFFER_SIZE;
2119 priv->fd = -1;
2120 priv->ready = PURPLE_XFER_READY_NONE;
2123 /* Called when done constructing */
2124 static void
2125 purple_xfer_constructed(GObject *object)
2127 PurpleXfer *xfer = PURPLE_XFER(object);
2128 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
2129 PurpleXferUiOps *ui_ops;
2131 parent_class->constructed(object);
2133 ui_ops = purple_xfers_get_ui_ops();
2135 if (ui_ops && ui_ops->data_not_sent) {
2136 /* If the ui will handle unsent data no need for buffer */
2137 priv->buffer = NULL;
2138 } else {
2139 priv->buffer = g_byte_array_sized_new(FT_INITIAL_BUFFER_SIZE);
2142 if (ui_ops != NULL && ui_ops->new_xfer != NULL)
2143 ui_ops->new_xfer(xfer);
2145 xfers = g_list_prepend(xfers, xfer);
2148 /* GObject finalize function */
2149 static void
2150 purple_xfer_finalize(GObject *object)
2152 PurpleXfer *xfer = PURPLE_XFER(object);
2153 PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer);
2154 PurpleXferUiOps *ui_ops;
2156 /* Close the file browser, if it's open */
2157 purple_request_close_with_handle(xfer);
2159 if (purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_STARTED)
2160 purple_xfer_cancel_local(xfer);
2162 ui_ops = purple_xfer_get_ui_ops(xfer);
2164 if (ui_ops != NULL && ui_ops->destroy != NULL)
2165 ui_ops->destroy(xfer);
2167 xfers = g_list_remove(xfers, xfer);
2169 g_free(priv->who);
2170 g_free(priv->filename);
2171 g_free(priv->remote_ip);
2172 g_free(priv->local_filename);
2174 if (priv->buffer)
2175 g_byte_array_free(priv->buffer, TRUE);
2177 g_free(priv->thumbnail_data);
2178 g_free(priv->thumbnail_mimetype);
2180 PURPLE_DBUS_UNREGISTER_POINTER(xfer);
2182 parent_class->finalize(object);
2185 /* Class initializer function */
2186 static void
2187 purple_xfer_class_init(PurpleXferClass *klass)
2189 GObjectClass *obj_class = G_OBJECT_CLASS(klass);
2191 parent_class = g_type_class_peek_parent(klass);
2193 obj_class->finalize = purple_xfer_finalize;
2194 obj_class->constructed = purple_xfer_constructed;
2196 /* Setup properties */
2197 obj_class->get_property = purple_xfer_get_property;
2198 obj_class->set_property = purple_xfer_set_property;
2200 g_type_class_add_private(klass, sizeof(PurpleXferPrivate));
2202 properties[PROP_TYPE] = g_param_spec_enum("type", "Transfer type",
2203 "The type of file transfer.", PURPLE_TYPE_XFER_TYPE,
2204 PURPLE_XFER_TYPE_UNKNOWN,
2205 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
2206 G_PARAM_STATIC_STRINGS);
2208 properties[PROP_ACCOUNT] = g_param_spec_object("account", "Account",
2209 "The account sending or receiving the file.",
2210 PURPLE_TYPE_ACCOUNT,
2211 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
2212 G_PARAM_STATIC_STRINGS);
2214 properties[PROP_REMOTE_USER] = g_param_spec_string("remote-user",
2215 "Remote user",
2216 "The name of the remote user.", NULL,
2217 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
2219 properties[PROP_MESSAGE] = g_param_spec_string("message", "Message",
2220 "The message for the file transfer.", NULL,
2221 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
2223 properties[PROP_FILENAME] = g_param_spec_string("filename", "Filename",
2224 "The filename for the file transfer.", NULL,
2225 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
2227 properties[PROP_LOCAL_FILENAME] = g_param_spec_string("local-filename",
2228 "Local filename",
2229 "The local filename for the file transfer.", NULL,
2230 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
2232 properties[PROP_FILE_SIZE] = g_param_spec_int64("file-size", "File size",
2233 "Size of the file in a file transfer.",
2234 G_MININT64, G_MAXINT64, 0,
2235 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
2237 properties[PROP_REMOTE_IP] = g_param_spec_string("remote-ip", "Remote IP",
2238 "The remote IP address in the file transfer.", NULL,
2239 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
2241 properties[PROP_LOCAL_PORT] = g_param_spec_int("local-port", "Local port",
2242 "The local port number in the file transfer.",
2243 G_MININT, G_MAXINT, 0,
2244 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
2246 properties[PROP_REMOTE_PORT] = g_param_spec_int("remote-port",
2247 "Remote port",
2248 "The remote port number in the file transfer.",
2249 G_MININT, G_MAXINT, 0,
2250 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
2252 properties[PROP_FD] = g_param_spec_int("fd", "Socket FD",
2253 "The socket file descriptor.",
2254 G_MININT, G_MAXINT, 0,
2255 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
2257 properties[PROP_WATCHER] = g_param_spec_int("watcher", "Watcher",
2258 "The watcher for the file transfer.",
2259 G_MININT, G_MAXINT, 0,
2260 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
2262 properties[PROP_BYTES_SENT] = g_param_spec_int64("bytes-sent", "Bytes sent",
2263 "The number of bytes sent (or received) so far.",
2264 G_MININT64, G_MAXINT64, 0,
2265 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
2267 properties[PROP_START_TIME] =
2268 #if SIZEOF_TIME_T == 4
2269 g_param_spec_int
2270 #elif SIZEOF_TIME_T == 8
2271 g_param_spec_int64
2272 #else
2273 #error Unknown size of time_t
2274 #endif
2275 ("start-time", "Start time",
2276 "The time the transfer of a file started.",
2277 #if SIZEOF_TIME_T == 4
2278 G_MININT, G_MAXINT, 0,
2279 #elif SIZEOF_TIME_T == 8
2280 G_MININT64, G_MAXINT64, 0,
2281 #else
2282 #error Unknown size of time_t
2283 #endif
2284 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
2286 properties[PROP_END_TIME] =
2287 #if SIZEOF_TIME_T == 4
2288 g_param_spec_int
2289 #elif SIZEOF_TIME_T == 8
2290 g_param_spec_int64
2291 #else
2292 #error Unknown size of time_t
2293 #endif
2294 ("end-time", "End time",
2295 "The time the transfer of a file ended.",
2296 #if SIZEOF_TIME_T == 4
2297 G_MININT, G_MAXINT, 0,
2298 #elif SIZEOF_TIME_T == 8
2299 G_MININT64, G_MAXINT64, 0,
2300 #else
2301 #error Unknown size of time_t
2302 #endif
2303 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
2305 properties[PROP_STATUS] = g_param_spec_enum("status", "Status",
2306 "The current status for the file transfer.",
2307 PURPLE_TYPE_XFER_STATUS, PURPLE_XFER_STATUS_UNKNOWN,
2308 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
2310 g_object_class_install_properties(obj_class, PROP_LAST, properties);
2313 GType
2314 purple_xfer_get_type(void)
2316 static GType type = 0;
2318 if(type == 0) {
2319 static const GTypeInfo info = {
2320 sizeof(PurpleXferClass),
2321 NULL,
2322 NULL,
2323 (GClassInitFunc)purple_xfer_class_init,
2324 NULL,
2325 NULL,
2326 sizeof(PurpleXfer),
2328 (GInstanceInitFunc)purple_xfer_init,
2329 NULL,
2332 type = g_type_register_static(G_TYPE_OBJECT, "PurpleXfer",
2333 &info, 0);
2336 return type;
2339 PurpleXfer *
2340 purple_xfer_new(PurpleAccount *account, PurpleXferType type, const char *who)
2342 PurpleXfer *xfer;
2343 PurpleProtocol *protocol;
2345 g_return_val_if_fail(type != PURPLE_XFER_TYPE_UNKNOWN, NULL);
2346 g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
2347 g_return_val_if_fail(who != NULL, NULL);
2349 protocol = purple_protocols_find(purple_account_get_protocol_id(account));
2351 g_return_val_if_fail(PURPLE_IS_PROTOCOL(protocol), NULL);
2353 if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, FACTORY_IFACE, xfer_new))
2354 xfer = purple_protocol_factory_iface_xfer_new(protocol, account, type,
2355 who);
2356 else
2357 xfer = g_object_new(PURPLE_TYPE_XFER,
2358 "account", account,
2359 "type", type,
2360 "remote-user", who,
2361 NULL
2364 g_return_val_if_fail(xfer != NULL, NULL);
2366 return xfer;
2369 /**************************************************************************
2370 * File Transfer Subsystem API
2371 **************************************************************************/
2372 GList *
2373 purple_xfers_get_all()
2375 return xfers;
2378 void *
2379 purple_xfers_get_handle(void) {
2380 static int handle = 0;
2382 return &handle;
2385 void
2386 purple_xfers_init(void) {
2387 void *handle = purple_xfers_get_handle();
2389 /* register signals */
2390 purple_signal_register(handle, "file-recv-accept",
2391 purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
2392 PURPLE_TYPE_XFER);
2393 purple_signal_register(handle, "file-send-accept",
2394 purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
2395 PURPLE_TYPE_XFER);
2396 purple_signal_register(handle, "file-recv-start",
2397 purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
2398 PURPLE_TYPE_XFER);
2399 purple_signal_register(handle, "file-send-start",
2400 purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
2401 PURPLE_TYPE_XFER);
2402 purple_signal_register(handle, "file-send-cancel",
2403 purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
2404 PURPLE_TYPE_XFER);
2405 purple_signal_register(handle, "file-recv-cancel",
2406 purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
2407 PURPLE_TYPE_XFER);
2408 purple_signal_register(handle, "file-send-complete",
2409 purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
2410 PURPLE_TYPE_XFER);
2411 purple_signal_register(handle, "file-recv-complete",
2412 purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
2413 PURPLE_TYPE_XFER);
2414 purple_signal_register(handle, "file-recv-request",
2415 purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
2416 PURPLE_TYPE_XFER);
2419 void
2420 purple_xfers_uninit(void)
2422 void *handle = purple_xfers_get_handle();
2424 purple_signals_disconnect_by_handle(handle);
2425 purple_signals_unregister_by_instance(handle);
2428 void
2429 purple_xfers_set_ui_ops(PurpleXferUiOps *ops) {
2430 xfer_ui_ops = ops;
2433 PurpleXferUiOps *
2434 purple_xfers_get_ui_ops(void) {
2435 return xfer_ui_ops;
2438 /**************************************************************************
2439 * GBoxed code
2440 **************************************************************************/
2441 static PurpleXferUiOps *
2442 purple_xfer_ui_ops_copy(PurpleXferUiOps *ops)
2444 PurpleXferUiOps *ops_new;
2446 g_return_val_if_fail(ops != NULL, NULL);
2448 ops_new = g_new(PurpleXferUiOps, 1);
2449 *ops_new = *ops;
2451 return ops_new;
2454 GType
2455 purple_xfer_ui_ops_get_type(void)
2457 static GType type = 0;
2459 if (type == 0) {
2460 type = g_boxed_type_register_static("PurpleXferUiOps",
2461 (GBoxedCopyFunc)purple_xfer_ui_ops_copy,
2462 (GBoxedFreeFunc)g_free);
2465 return type;