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
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
23 #include "glibcompat.h"
25 #include "dbus-maybe.h"
27 #include "image-store.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
;
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
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.
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
);
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.
108 PURPLE_XFER_READY_NONE
= 0x0,
109 PURPLE_XFER_READY_UI
= 0x1,
110 PURPLE_XFER_READY_PROTOCOL
= 0x2,
113 /* TODO: Should really use a PurpleCircBuffer for this. */
116 gpointer thumbnail_data
; /* thumbnail image */
117 gsize thumbnail_size
;
118 gchar
*thumbnail_mimetype
;
121 /* GObject property enums */
144 static GObjectClass
*parent_class
;
145 static GParamSpec
*properties
[PROP_LAST
];
147 static int purple_xfer_choose_file(PurpleXfer
*xfer
);
150 purple_xfer_status_type_to_string(PurpleXferStatus type
)
152 static const struct {
153 PurpleXferStatus type
;
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" }
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";
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
)
188 priv
->status
= status
;
190 g_object_notify_by_pspec(G_OBJECT(xfer
), properties
[PROP_STATUS
]);
192 if(priv
->type
== PURPLE_XFER_TYPE_SEND
) {
194 case PURPLE_XFER_STATUS_ACCEPTED
:
195 purple_signal_emit(purple_xfers_get_handle(), "file-send-accept", xfer
);
197 case PURPLE_XFER_STATUS_STARTED
:
198 purple_signal_emit(purple_xfers_get_handle(), "file-send-start", xfer
);
200 case PURPLE_XFER_STATUS_DONE
:
201 purple_signal_emit(purple_xfers_get_handle(), "file-send-complete", xfer
);
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
);
210 } else if(priv
->type
== PURPLE_XFER_TYPE_RECEIVE
) {
212 case PURPLE_XFER_STATUS_ACCEPTED
:
213 purple_signal_emit(purple_xfers_get_handle(), "file-recv-accept", xfer
);
215 case PURPLE_XFER_STATUS_STARTED
:
216 purple_signal_emit(purple_xfers_get_handle(), "file-recv-start", xfer
);
218 case PURPLE_XFER_STATUS_DONE
:
219 purple_signal_emit(purple_xfers_get_handle(), "file-recv-complete", xfer
);
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
);
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
;
238 gconstpointer thumbnail_data
;
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
));
253 escaped
= g_markup_escape_text(message
, -1);
256 flags
|= PURPLE_MESSAGE_ERROR
;
258 if (print_thumbnail
&& thumbnail_data
) {
259 gchar
*message_with_img
;
260 gpointer data
= g_memdup(thumbnail_data
, size
);
264 img
= purple_image_new_from_data(data
, size
);
265 id
= purple_image_store_add(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
);
274 purple_conversation_write_system_message(
275 PURPLE_CONVERSATION(im
), escaped
, flags
);
281 purple_xfer_conversation_write(PurpleXfer
*xfer
, const gchar
*message
,
284 purple_xfer_conversation_write_internal(xfer
, message
, is_error
, FALSE
);
287 /* maybe this one should be exported publically? */
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
)
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
);
306 case PURPLE_XFER_TYPE_SEND
:
307 msg
= g_strdup_printf(_("Error reading %s: \n%s.\n"),
308 utf8
, g_strerror(err
));
310 case PURPLE_XFER_TYPE_RECEIVE
:
311 msg
= g_strdup_printf(_("Error writing %s: \n%s.\n"),
312 utf8
, g_strerror(err
));
315 msg
= g_strdup_printf(_("Error accessing %s: \n%s.\n"),
316 utf8
, g_strerror(err
));
321 purple_xfer_conversation_write(xfer
, msg
, TRUE
);
322 purple_xfer_error(xfer_type
, account
, priv
->who
, msg
);
327 purple_xfer_choose_file_ok_cb(void *user_data
, const char *filename
)
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
) {
345 dir
= g_path_get_dirname(filename
);
347 if (g_access(dir
, mode
) == 0) {
348 purple_xfer_request_accepted(xfer
, filename
);
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
);
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
)) {
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
);
391 purple_notify_error(NULL
, NULL
, msg
, NULL
,
392 purple_request_cpar_from_account(
393 purple_xfer_get_account(xfer
)));
395 purple_xfer_request_denied(xfer
);
397 else if (type
== PURPLE_XFER_TYPE_SEND
) {
404 if (g_access(filename
, mode
) == 0) {
405 purple_xfer_request_accepted(xfer
, filename
);
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
);
417 purple_xfer_request_accepted(xfer
, filename
);
420 g_object_unref(xfer
);
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
);
432 purple_xfer_request_denied(xfer
);
433 g_object_unref(xfer
);
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
)),
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
);
460 purple_xfer_ask_recv(PurpleXfer
*xfer
)
462 PurpleXferPrivate
*priv
= PURPLE_XFER_GET_PRIVATE(xfer
);
463 char *buf
, *size_buf
;
468 /* If we have already accepted the request, ask the destination file
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
);
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
,
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
));
506 purple_xfer_choose_file(xfer
);
510 ask_accept_ok(PurpleXfer
*xfer
)
512 purple_xfer_request_accepted(xfer
, NULL
);
518 ask_accept_cancel(PurpleXfer
*xfer
)
520 purple_xfer_request_denied(xfer
);
521 g_object_unref(xfer
);
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
));
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
);
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
);
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
);
584 purple_xfer_ask_accept(xfer
);
589 purple_xfer_choose_file(xfer
);
594 purple_xfer_request_accepted(PurpleXfer
*xfer
, const char *filename
)
596 PurpleXferPrivate
*priv
= PURPLE_XFER_GET_PRIVATE(xfer
);
599 char *msg
, *utf8
, *base
;
600 PurpleAccount
*account
;
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
);
616 g_return_if_fail(filename
!= NULL
);
619 buddy
= purple_blist_find_buddy(account
, priv
->who
);
621 if (type
== PURPLE_XFER_TYPE_SEND
) {
623 /* Check the filename. */
624 PurpleXferUiOps
*ui_ops
;
625 ui_ops
= purple_xfer_get_ui_ops(xfer
);
628 if (g_strrstr(filename
, "../") || g_strrstr(filename
, "..\\"))
630 if (g_strrstr(filename
, "../"))
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
);
640 g_object_unref(xfer
);
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
);
651 purple_xfer_set_local_filename(xfer
, filename
);
652 purple_xfer_set_size(xfer
, st
.st_size
);
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
);
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
);
665 purple_xfer_conversation_write(xfer
, msg
, FALSE
);
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
);
679 purple_xfer_add(xfer
);
680 priv
->ops
.init(xfer
);
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);
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
;
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
);
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
;
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
);
745 priv
->who
= g_strdup(who
);
747 g_object_notify_by_pspec(G_OBJECT(xfer
), properties
[PROP_REMOTE_USER
]);
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
);
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
);
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
))
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
);
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
;
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
;
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
;
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
;
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);
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)
848 return ((double)purple_xfer_get_bytes_sent(xfer
) /
849 (double)purple_xfer_get_size(xfer
));
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
;
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
;
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
;
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
;
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
);
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
]);
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
) {
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"),
950 msg
= g_strdup_printf(_("Transfer of file %s complete"),
955 msg
= g_strdup(_("File transfer complete"));
957 im
= purple_conversations_find_im_with_account(priv
->who
,
958 purple_xfer_get_account(xfer
));
961 purple_conversation_write_system_message(
962 PURPLE_CONVERSATION(im
), msg
, 0);
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
));
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
]);
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
;
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
]);
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
]);
1023 purple_xfer_set_size(PurpleXfer
*xfer
, goffset size
)
1025 PurpleXferPrivate
*priv
= PURPLE_XFER_GET_PRIVATE(xfer
);
1027 g_return_if_fail(priv
!= NULL
);
1031 g_object_notify_by_pspec(G_OBJECT(xfer
), properties
[PROP_FILE_SIZE
]);
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
]);
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
]);
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
;
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
;
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
;
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
;
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
;
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
;
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
;
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
;
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
;
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
);
1169 purple_xfer_read(PurpleXfer
*xfer
, guchar
**buffer
)
1171 PurpleXferPrivate
*priv
= PURPLE_XFER_GET_PRIVATE(xfer
);
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
;
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
);
1186 *buffer
= g_malloc0(s
);
1188 r
= read(priv
->fd
, *buffer
, s
);
1189 if (r
< 0 && errno
== EAGAIN
)
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
1203 purple_xfer_increase_buffer_size(xfer
);
1209 do_write(PurpleXfer
*xfer
, const guchar
*buffer
, gsize size
)
1211 PurpleXferPrivate
*priv
= PURPLE_XFER_GET_PRIVATE(xfer
);
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
);
1221 r
= write(priv
->fd
, buffer
, size
);
1222 if (r
< 0 && errno
== EAGAIN
)
1230 purple_xfer_write(PurpleXfer
*xfer
, const guchar
*buffer
, gsize size
)
1232 PurpleXferPrivate
*priv
= PURPLE_XFER_GET_PRIVATE(xfer
);
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
);
1243 purple_xfer_write_file(PurpleXfer
*xfer
, const guchar
*buffer
, gsize size
)
1245 PurpleXferPrivate
*priv
= PURPLE_XFER_GET_PRIVATE(xfer
);
1246 PurpleXferUiOps
*ui_ops
;
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
);
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
);
1272 wc
= fwrite(buffer
, 1, size
, priv
->dest_fp
);
1276 purple_debug_error("xfer",
1277 "Unable to write whole buffer.\n");
1278 purple_xfer_cancel_local(xfer
);
1282 purple_xfer_set_bytes_sent(xfer
, purple_xfer_get_bytes_sent(xfer
) +
1289 purple_xfer_read_file(PurpleXfer
*xfer
, guchar
*buffer
, gsize size
)
1291 PurpleXferPrivate
*priv
= PURPLE_XFER_GET_PRIVATE(xfer
);
1292 PurpleXferUiOps
*ui_ops
;
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
) {
1307 purple_debug_error("xfer",
1308 "Got too much data from UI.\n");
1309 purple_xfer_cancel_local(xfer
);
1314 memcpy(buffer
, buffer_got
, got_len
);
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
);
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
);
1335 purple_xfer_set_bytes_sent(xfer
,
1336 purple_xfer_get_bytes_sent(xfer
) + got_len
);
1343 do_transfer(PurpleXfer
*xfer
)
1345 PurpleXferPrivate
*priv
= PURPLE_XFER_GET_PRIVATE(xfer
);
1346 PurpleXferUiOps
*ui_ops
;
1347 guchar
*buffer
= NULL
;
1350 ui_ops
= purple_xfer_get_ui_ops(xfer
);
1352 if (priv
->type
== PURPLE_XFER_TYPE_RECEIVE
) {
1353 r
= purple_xfer_read(xfer
, &buffer
);
1355 if (!purple_xfer_write_file(xfer
, buffer
, r
)) {
1361 purple_xfer_cancel_remote(xfer
);
1365 } else if (priv
->type
== PURPLE_XFER_TYPE_SEND
) {
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. */
1373 if (priv
->watcher
) {
1374 purple_input_remove(priv
->watcher
);
1375 purple_xfer_set_watcher(xfer
, 0);
1381 if (priv
->buffer
->len
< s
) {
1382 s
-= priv
->buffer
->len
;
1390 buffer
= g_new(guchar
, s
);
1391 result
= purple_xfer_read_file(xfer
, buffer
, s
);
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();
1413 g_byte_array_append(priv
->buffer
, buffer
, result
);
1415 buffer
= priv
->buffer
->data
;
1416 result
= priv
->buffer
->len
;
1419 r
= do_write(xfer
, buffer
, result
);
1422 purple_xfer_cancel_remote(xfer
);
1424 /* We don't free buffer if priv->buffer is set, because in
1425 that case buffer doesn't belong to us. */
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
1434 purple_xfer_increase_buffer_size(xfer
);
1436 if (ui_ops
&& ui_ops
->data_not_sent
)
1437 ui_ops
->data_not_sent(xfer
, buffer
+ r
, result
- r
);
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.
1447 g_byte_array_remove_range(priv
->buffer
, 0, r
);
1452 if (priv
->ops
.ack
!= NULL
)
1453 priv
->ops
.ack(xfer
, buffer
, r
);
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
);
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
);
1493 priv
->ready
= PURPLE_XFER_READY_NONE
;
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
);
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
);
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
);
1542 connect_cb(gpointer data
, gint source
, const gchar
*error_message
)
1544 PurpleXfer
*xfer
= PURPLE_XFER(data
);
1547 purple_xfer_cancel_local(xfer
);
1551 purple_xfer_set_fd(xfer
, source
);
1552 begin_transfer(xfer
, PURPLE_INPUT_READ
);
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
);
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
;
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
);
1603 purple_debug_misc("xfer", "Protocol (and UI) ready on ft %p, so proceeding\n", xfer
);
1605 priv
->ready
= PURPLE_XFER_READY_NONE
;
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
;
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
;
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
);
1645 purple_xfer_set_fd(xfer
, fd
);
1649 cond
= PURPLE_INPUT_WRITE
;
1651 purple_xfer_set_fd(xfer
, fd
);
1654 begin_transfer(xfer
, cond
);
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
);
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",
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",
1694 priv
->dest_fp
= NULL
;
1697 g_object_unref(xfer
);
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
);
1714 purple_xfer_cancel_local(PurpleXfer
*xfer
)
1716 PurpleXferPrivate
*priv
= PURPLE_XFER_GET_PRIVATE(xfer
);
1717 PurpleXferUiOps
*ui_ops
;
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
));
1748 msg
= g_strdup(_("File transfer cancelled"));
1750 purple_xfer_conversation_write(xfer
, msg
, FALSE
);
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
);
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);
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
);
1786 purple_xfer_cancel_remote(PurpleXfer
*xfer
)
1788 PurpleXferPrivate
*priv
= PURPLE_XFER_GET_PRIVATE(xfer
);
1789 PurpleXferUiOps
*ui_ops
;
1791 PurpleAccount
*account
;
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
));
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
);
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
);
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);
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
);
1852 purple_xfer_error(PurpleXferType type
, PurpleAccount
*account
, const char *who
, const char *msg
)
1856 g_return_if_fail(msg
!= NULL
);
1857 g_return_if_fail(type
!= PURPLE_XFER_TYPE_UNKNOWN
);
1861 buddy
= purple_blist_find_buddy(account
, who
);
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
);
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
));
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
));
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
);
1897 *len
= priv
->thumbnail_size
;
1899 return priv
->thumbnail_data
;
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
;
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
);
1928 priv
->thumbnail_data
= NULL
;
1929 priv
->thumbnail_size
= 0;
1930 priv
->thumbnail_mimetype
= NULL
;
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
);
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
;
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 /**************************************************************************
1982 **************************************************************************/
1983 /* Set method for GObject properties */
1985 purple_xfer_set_property(GObject
*obj
, guint param_id
, const GValue
*value
,
1988 PurpleXfer
*xfer
= PURPLE_XFER(obj
);
1989 PurpleXferPrivate
*priv
= PURPLE_XFER_GET_PRIVATE(xfer
);
1993 priv
->type
= g_value_get_enum(value
);
1996 priv
->account
= g_value_get_object(value
);
1998 case PROP_REMOTE_USER
:
1999 purple_xfer_set_remote_user(xfer
, g_value_get_string(value
));
2002 purple_xfer_set_message(xfer
, g_value_get_string(value
));
2005 purple_xfer_set_filename(xfer
, g_value_get_string(value
));
2007 case PROP_LOCAL_FILENAME
:
2008 purple_xfer_set_local_filename(xfer
, g_value_get_string(value
));
2010 case PROP_FILE_SIZE
:
2011 purple_xfer_set_size(xfer
, g_value_get_int64(value
));
2013 case PROP_LOCAL_PORT
:
2014 purple_xfer_set_local_port(xfer
, g_value_get_int(value
));
2017 purple_xfer_set_fd(xfer
, g_value_get_int(value
));
2020 purple_xfer_set_watcher(xfer
, g_value_get_int(value
));
2022 case PROP_BYTES_SENT
:
2023 purple_xfer_set_bytes_sent(xfer
, g_value_get_int64(value
));
2026 purple_xfer_set_status(xfer
, g_value_get_enum(value
));
2029 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj
, param_id
, pspec
);
2034 /* Get method for GObject properties */
2036 purple_xfer_get_property(GObject
*obj
, guint param_id
, GValue
*value
,
2039 PurpleXfer
*xfer
= PURPLE_XFER(obj
);
2043 g_value_set_enum(value
, purple_xfer_get_xfer_type(xfer
));
2046 g_value_set_object(value
, purple_xfer_get_account(xfer
));
2048 case PROP_REMOTE_USER
:
2049 g_value_set_string(value
, purple_xfer_get_remote_user(xfer
));
2052 g_value_set_string(value
, purple_xfer_get_message(xfer
));
2055 g_value_set_string(value
, purple_xfer_get_filename(xfer
));
2057 case PROP_LOCAL_FILENAME
:
2058 g_value_set_string(value
, purple_xfer_get_local_filename(xfer
));
2060 case PROP_FILE_SIZE
:
2061 g_value_set_int64(value
, purple_xfer_get_size(xfer
));
2063 case PROP_REMOTE_IP
:
2064 g_value_set_string(value
, purple_xfer_get_remote_ip(xfer
));
2066 case PROP_LOCAL_PORT
:
2067 g_value_set_int(value
, purple_xfer_get_local_port(xfer
));
2069 case PROP_REMOTE_PORT
:
2070 g_value_set_int(value
, purple_xfer_get_remote_port(xfer
));
2073 g_value_set_int(value
, purple_xfer_get_fd(xfer
));
2076 g_value_set_int(value
, purple_xfer_get_watcher(xfer
));
2078 case PROP_BYTES_SENT
:
2079 g_value_set_int64(value
, purple_xfer_get_bytes_sent(xfer
));
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
));
2087 #error Unknown size of time_t
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
));
2096 #error Unknown size of time_t
2100 g_value_set_enum(value
, purple_xfer_get_status(xfer
));
2103 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj
, param_id
, pspec
);
2108 /* GObject initialization function */
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
;
2120 priv
->ready
= PURPLE_XFER_READY_NONE
;
2123 /* Called when done constructing */
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
;
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 */
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
);
2170 g_free(priv
->filename
);
2171 g_free(priv
->remote_ip
);
2172 g_free(priv
->local_filename
);
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 */
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",
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",
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",
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
2270 #elif SIZEOF_TIME_T == 8
2273 #error Unknown size of time_t
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,
2282 #error Unknown size of time_t
2284 G_PARAM_READABLE
| G_PARAM_STATIC_STRINGS
);
2286 properties
[PROP_END_TIME
] =
2287 #if SIZEOF_TIME_T == 4
2289 #elif SIZEOF_TIME_T == 8
2292 #error Unknown size of time_t
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,
2301 #error Unknown size of time_t
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
);
2314 purple_xfer_get_type(void)
2316 static GType type
= 0;
2319 static const GTypeInfo info
= {
2320 sizeof(PurpleXferClass
),
2323 (GClassInitFunc
)purple_xfer_class_init
,
2328 (GInstanceInitFunc
)purple_xfer_init
,
2332 type
= g_type_register_static(G_TYPE_OBJECT
, "PurpleXfer",
2340 purple_xfer_new(PurpleAccount
*account
, PurpleXferType type
, const char *who
)
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
,
2357 xfer
= g_object_new(PURPLE_TYPE_XFER
,
2364 g_return_val_if_fail(xfer
!= NULL
, NULL
);
2369 /**************************************************************************
2370 * File Transfer Subsystem API
2371 **************************************************************************/
2373 purple_xfers_get_all()
2379 purple_xfers_get_handle(void) {
2380 static int handle
= 0;
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,
2393 purple_signal_register(handle
, "file-send-accept",
2394 purple_marshal_VOID__POINTER
, G_TYPE_NONE
, 1,
2396 purple_signal_register(handle
, "file-recv-start",
2397 purple_marshal_VOID__POINTER
, G_TYPE_NONE
, 1,
2399 purple_signal_register(handle
, "file-send-start",
2400 purple_marshal_VOID__POINTER
, G_TYPE_NONE
, 1,
2402 purple_signal_register(handle
, "file-send-cancel",
2403 purple_marshal_VOID__POINTER
, G_TYPE_NONE
, 1,
2405 purple_signal_register(handle
, "file-recv-cancel",
2406 purple_marshal_VOID__POINTER
, G_TYPE_NONE
, 1,
2408 purple_signal_register(handle
, "file-send-complete",
2409 purple_marshal_VOID__POINTER
, G_TYPE_NONE
, 1,
2411 purple_signal_register(handle
, "file-recv-complete",
2412 purple_marshal_VOID__POINTER
, G_TYPE_NONE
, 1,
2414 purple_signal_register(handle
, "file-recv-request",
2415 purple_marshal_VOID__POINTER
, G_TYPE_NONE
, 1,
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
);
2429 purple_xfers_set_ui_ops(PurpleXferUiOps
*ops
) {
2434 purple_xfers_get_ui_ops(void) {
2438 /**************************************************************************
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);
2455 purple_xfer_ui_ops_get_type(void)
2457 static GType type
= 0;
2460 type
= g_boxed_type_register_static("PurpleXferUiOps",
2461 (GBoxedCopyFunc
)purple_xfer_ui_ops_copy
,
2462 (GBoxedFreeFunc
)g_free
);