2 * @file ft.c File Transfer API
7 * Purple is the legal property of its developers, whose names are too numerous
8 * to list here. Please refer to the COPYRIGHT file distributed with this
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
27 #include "dbus-maybe.h"
37 #define FT_INITIAL_BUFFER_SIZE 4096
38 #define FT_MAX_BUFFER_SIZE 65535
40 static PurpleXferUiOps
*xfer_ui_ops
= NULL
;
44 * A hack to store more data since we can't extend the size of PurpleXfer
47 static GHashTable
*xfers_data
= NULL
;
49 typedef struct _PurpleXferPrivData
{
51 * Used to moderate the file transfer when either the read/write ui_ops are
52 * set or fd is not set. In those cases, the UI/prpl call the respective
53 * function, which is somewhat akin to a fd watch being triggered.
56 PURPLE_XFER_READY_NONE
= 0x0,
57 PURPLE_XFER_READY_UI
= 0x1,
58 PURPLE_XFER_READY_PRPL
= 0x2,
61 /* TODO: Should really use a PurpleCircBuffer for this. */
64 gpointer thumbnail_data
; /**< thumbnail image */
66 gchar
*thumbnail_mimetype
;
69 static int purple_xfer_choose_file(PurpleXfer
*xfer
);
72 purple_xfer_priv_data_destroy(gpointer data
)
74 PurpleXferPrivData
*priv
= data
;
77 g_byte_array_free(priv
->buffer
, TRUE
);
79 g_free(priv
->thumbnail_data
);
81 g_free(priv
->thumbnail_mimetype
);
87 purple_xfer_status_type_to_string(PurpleXferStatusType type
)
90 PurpleXferStatusType type
;
93 { PURPLE_XFER_STATUS_UNKNOWN
, "unknown" },
94 { PURPLE_XFER_STATUS_NOT_STARTED
, "not started" },
95 { PURPLE_XFER_STATUS_ACCEPTED
, "accepted" },
96 { PURPLE_XFER_STATUS_STARTED
, "started" },
97 { PURPLE_XFER_STATUS_DONE
, "done" },
98 { PURPLE_XFER_STATUS_CANCEL_LOCAL
, "cancelled locally" },
99 { PURPLE_XFER_STATUS_CANCEL_REMOTE
, "cancelled remotely" }
103 for (i
= 0; i
< G_N_ELEMENTS(type_names
); ++i
)
104 if (type_names
[i
].type
== type
)
105 return type_names
[i
].name
;
107 return "invalid state";
111 purple_xfers_get_all()
117 purple_xfer_new(PurpleAccount
*account
, PurpleXferType type
, const char *who
)
120 PurpleXferUiOps
*ui_ops
;
121 PurpleXferPrivData
*priv
;
123 g_return_val_if_fail(type
!= PURPLE_XFER_UNKNOWN
, NULL
);
124 g_return_val_if_fail(account
!= NULL
, NULL
);
125 g_return_val_if_fail(who
!= NULL
, NULL
);
127 xfer
= g_new0(PurpleXfer
, 1);
128 PURPLE_DBUS_REGISTER_POINTER(xfer
, PurpleXfer
);
132 xfer
->account
= account
;
133 xfer
->who
= g_strdup(who
);
134 xfer
->ui_ops
= ui_ops
= purple_xfers_get_ui_ops();
135 xfer
->message
= NULL
;
136 xfer
->current_buffer_size
= FT_INITIAL_BUFFER_SIZE
;
139 priv
= g_new0(PurpleXferPrivData
, 1);
140 priv
->ready
= PURPLE_XFER_READY_NONE
;
142 if (ui_ops
&& ui_ops
->data_not_sent
) {
143 /* If the ui will handle unsent data no need for buffer */
146 priv
->buffer
= g_byte_array_sized_new(FT_INITIAL_BUFFER_SIZE
);
149 g_hash_table_insert(xfers_data
, xfer
, priv
);
151 ui_ops
= purple_xfer_get_ui_ops(xfer
);
153 if (ui_ops
!= NULL
&& ui_ops
->new_xfer
!= NULL
)
154 ui_ops
->new_xfer(xfer
);
156 xfers
= g_list_prepend(xfers
, xfer
);
158 if (purple_debug_is_verbose())
159 purple_debug_info("xfer", "new %p [%d]\n", xfer
, xfer
->ref
);
165 purple_xfer_destroy(PurpleXfer
*xfer
)
167 PurpleXferUiOps
*ui_ops
;
169 g_return_if_fail(xfer
!= NULL
);
171 if (purple_debug_is_verbose())
172 purple_debug_info("xfer", "destroyed %p [%d]\n", xfer
, xfer
->ref
);
174 /* Close the file browser, if it's open */
175 purple_request_close_with_handle(xfer
);
177 if (purple_xfer_get_status(xfer
) == PURPLE_XFER_STATUS_STARTED
)
178 purple_xfer_cancel_local(xfer
);
180 ui_ops
= purple_xfer_get_ui_ops(xfer
);
182 if (ui_ops
!= NULL
&& ui_ops
->destroy
!= NULL
)
183 ui_ops
->destroy(xfer
);
186 g_free(xfer
->filename
);
187 g_free(xfer
->remote_ip
);
188 g_free(xfer
->local_filename
);
190 g_hash_table_remove(xfers_data
, xfer
);
192 PURPLE_DBUS_UNREGISTER_POINTER(xfer
);
193 xfers
= g_list_remove(xfers
, xfer
);
198 purple_xfer_ref(PurpleXfer
*xfer
)
200 g_return_if_fail(xfer
!= NULL
);
204 if (purple_debug_is_verbose())
205 purple_debug_info("xfer", "ref'd %p [%d]\n", xfer
, xfer
->ref
);
209 purple_xfer_unref(PurpleXfer
*xfer
)
211 g_return_if_fail(xfer
!= NULL
);
212 g_return_if_fail(xfer
->ref
> 0);
216 if (purple_debug_is_verbose())
217 purple_debug_info("xfer", "unref'd %p [%d]\n", xfer
, xfer
->ref
);
220 purple_xfer_destroy(xfer
);
224 purple_xfer_set_status(PurpleXfer
*xfer
, PurpleXferStatusType status
)
226 g_return_if_fail(xfer
!= NULL
);
228 if (purple_debug_is_verbose())
229 purple_debug_info("xfer", "Changing status of xfer %p from %s to %s\n",
230 xfer
, purple_xfer_status_type_to_string(xfer
->status
),
231 purple_xfer_status_type_to_string(status
));
233 if (xfer
->status
== status
)
236 xfer
->status
= status
;
238 if(xfer
->type
== PURPLE_XFER_SEND
) {
240 case PURPLE_XFER_STATUS_ACCEPTED
:
241 purple_signal_emit(purple_xfers_get_handle(), "file-send-accept", xfer
);
243 case PURPLE_XFER_STATUS_STARTED
:
244 purple_signal_emit(purple_xfers_get_handle(), "file-send-start", xfer
);
246 case PURPLE_XFER_STATUS_DONE
:
247 purple_signal_emit(purple_xfers_get_handle(), "file-send-complete", xfer
);
249 case PURPLE_XFER_STATUS_CANCEL_LOCAL
:
250 case PURPLE_XFER_STATUS_CANCEL_REMOTE
:
251 purple_signal_emit(purple_xfers_get_handle(), "file-send-cancel", xfer
);
256 } else if(xfer
->type
== PURPLE_XFER_RECEIVE
) {
258 case PURPLE_XFER_STATUS_ACCEPTED
:
259 purple_signal_emit(purple_xfers_get_handle(), "file-recv-accept", xfer
);
261 case PURPLE_XFER_STATUS_STARTED
:
262 purple_signal_emit(purple_xfers_get_handle(), "file-recv-start", xfer
);
264 case PURPLE_XFER_STATUS_DONE
:
265 purple_signal_emit(purple_xfers_get_handle(), "file-recv-complete", xfer
);
267 case PURPLE_XFER_STATUS_CANCEL_LOCAL
:
268 case PURPLE_XFER_STATUS_CANCEL_REMOTE
:
269 purple_signal_emit(purple_xfers_get_handle(), "file-recv-cancel", xfer
);
278 purple_xfer_conversation_write_internal(PurpleXfer
*xfer
,
279 const char *message
, gboolean is_error
, gboolean print_thumbnail
)
281 PurpleConversation
*conv
= NULL
;
282 PurpleMessageFlags flags
= PURPLE_MESSAGE_SYSTEM
;
284 gconstpointer thumbnail_data
;
287 g_return_if_fail(xfer
!= NULL
);
288 g_return_if_fail(message
!= NULL
);
290 thumbnail_data
= purple_xfer_get_thumbnail(xfer
, &size
);
292 conv
= purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM
, xfer
->who
,
293 purple_xfer_get_account(xfer
));
298 escaped
= g_markup_escape_text(message
, -1);
301 flags
|= PURPLE_MESSAGE_ERROR
;
303 if (print_thumbnail
&& thumbnail_data
) {
304 gchar
*message_with_img
;
305 gpointer data
= g_memdup(thumbnail_data
, size
);
306 int id
= purple_imgstore_add_with_id(data
, size
, NULL
);
309 g_strdup_printf("<img id='%d'> %s", id
, escaped
);
310 purple_conversation_write(conv
, NULL
, message_with_img
, flags
,
312 purple_imgstore_unref_by_id(id
);
313 g_free(message_with_img
);
315 purple_conversation_write(conv
, NULL
, escaped
, flags
, time(NULL
));
321 purple_xfer_conversation_write(PurpleXfer
*xfer
, gchar
*message
,
324 purple_xfer_conversation_write_internal(xfer
, message
, is_error
, FALSE
);
327 /* maybe this one should be exported publically? */
329 purple_xfer_conversation_write_with_thumbnail(PurpleXfer
*xfer
,
330 const gchar
*message
)
332 purple_xfer_conversation_write_internal(xfer
, message
, FALSE
, TRUE
);
336 static void purple_xfer_show_file_error(PurpleXfer
*xfer
, const char *filename
)
339 gchar
*msg
= NULL
, *utf8
;
340 PurpleXferType xfer_type
= purple_xfer_get_type(xfer
);
341 PurpleAccount
*account
= purple_xfer_get_account(xfer
);
343 utf8
= g_filename_to_utf8(filename
, -1, NULL
, NULL
, NULL
);
345 case PURPLE_XFER_SEND
:
346 msg
= g_strdup_printf(_("Error reading %s: \n%s.\n"),
347 utf8
, g_strerror(err
));
349 case PURPLE_XFER_RECEIVE
:
350 msg
= g_strdup_printf(_("Error writing %s: \n%s.\n"),
351 utf8
, g_strerror(err
));
354 msg
= g_strdup_printf(_("Error accessing %s: \n%s.\n"),
355 utf8
, g_strerror(err
));
360 purple_xfer_conversation_write(xfer
, msg
, TRUE
);
361 purple_xfer_error(xfer_type
, account
, xfer
->who
, msg
);
366 purple_xfer_choose_file_ok_cb(void *user_data
, const char *filename
)
373 xfer
= (PurpleXfer
*)user_data
;
374 type
= purple_xfer_get_type(xfer
);
376 if (g_stat(filename
, &st
) != 0) {
377 /* File not found. */
378 if (type
== PURPLE_XFER_RECEIVE
) {
384 dir
= g_path_get_dirname(filename
);
386 if (g_access(dir
, mode
) == 0) {
387 purple_xfer_request_accepted(xfer
, filename
);
389 purple_xfer_ref(xfer
);
390 purple_notify_message(
391 NULL
, PURPLE_NOTIFY_MSG_ERROR
, NULL
,
392 _("Directory is not writable."), NULL
,
393 (PurpleNotifyCloseCallback
)purple_xfer_choose_file
, xfer
);
399 purple_xfer_show_file_error(xfer
, filename
);
400 purple_xfer_cancel_local(xfer
);
403 else if ((type
== PURPLE_XFER_SEND
) && (st
.st_size
== 0)) {
405 purple_notify_error(NULL
, NULL
,
406 _("Cannot send a file of 0 bytes."), NULL
);
408 purple_xfer_cancel_local(xfer
);
410 else if ((type
== PURPLE_XFER_SEND
) && S_ISDIR(st
.st_mode
)) {
412 * XXX - Sending a directory should be valid for some protocols.
414 purple_notify_error(NULL
, NULL
,
415 _("Cannot send a directory."), NULL
);
417 purple_xfer_cancel_local(xfer
);
419 else if ((type
== PURPLE_XFER_RECEIVE
) && S_ISDIR(st
.st_mode
)) {
421 utf8
= g_filename_to_utf8(filename
, -1, NULL
, NULL
, NULL
);
422 msg
= g_strdup_printf(
423 _("%s is not a regular file. Cowardly refusing to overwrite it.\n"), utf8
);
425 purple_notify_error(NULL
, NULL
, msg
, NULL
);
427 purple_xfer_request_denied(xfer
);
429 else if (type
== PURPLE_XFER_SEND
) {
436 if (g_access(filename
, mode
) == 0) {
437 purple_xfer_request_accepted(xfer
, filename
);
439 purple_xfer_ref(xfer
);
440 purple_notify_message(
441 NULL
, PURPLE_NOTIFY_MSG_ERROR
, NULL
,
442 _("File is not readable."), NULL
,
443 (PurpleNotifyCloseCallback
)purple_xfer_choose_file
, xfer
);
447 purple_xfer_request_accepted(xfer
, filename
);
450 purple_xfer_unref(xfer
);
454 purple_xfer_choose_file_cancel_cb(void *user_data
, const char *filename
)
456 PurpleXfer
*xfer
= (PurpleXfer
*)user_data
;
458 purple_xfer_set_status(xfer
, PURPLE_XFER_STATUS_CANCEL_LOCAL
);
459 if (purple_xfer_get_type(xfer
) == PURPLE_XFER_SEND
)
460 purple_xfer_cancel_local(xfer
);
462 purple_xfer_request_denied(xfer
);
463 purple_xfer_unref(xfer
);
467 purple_xfer_choose_file(PurpleXfer
*xfer
)
469 purple_request_file(xfer
, NULL
, purple_xfer_get_filename(xfer
),
470 (purple_xfer_get_type(xfer
) == PURPLE_XFER_RECEIVE
),
471 G_CALLBACK(purple_xfer_choose_file_ok_cb
),
472 G_CALLBACK(purple_xfer_choose_file_cancel_cb
),
473 purple_xfer_get_account(xfer
), xfer
->who
, NULL
,
480 cancel_recv_cb(PurpleXfer
*xfer
)
482 purple_xfer_set_status(xfer
, PURPLE_XFER_STATUS_CANCEL_LOCAL
);
483 purple_xfer_request_denied(xfer
);
484 purple_xfer_unref(xfer
);
490 purple_xfer_ask_recv(PurpleXfer
*xfer
)
492 char *buf
, *size_buf
;
497 /* If we have already accepted the request, ask the destination file
499 if (purple_xfer_get_status(xfer
) != PURPLE_XFER_STATUS_ACCEPTED
) {
500 PurpleBuddy
*buddy
= purple_find_buddy(xfer
->account
, xfer
->who
);
502 if (purple_xfer_get_filename(xfer
) != NULL
)
504 size
= purple_xfer_get_size(xfer
);
505 size_buf
= purple_str_size_to_units(size
);
506 buf
= g_strdup_printf(_("%s wants to send you %s (%s)"),
507 buddy
? purple_buddy_get_alias(buddy
) : xfer
->who
,
508 purple_xfer_get_filename(xfer
), size_buf
);
513 buf
= g_strdup_printf(_("%s wants to send you a file"),
514 buddy
? purple_buddy_get_alias(buddy
) : xfer
->who
);
517 if (xfer
->message
!= NULL
)
518 serv_got_im(purple_account_get_connection(xfer
->account
),
519 xfer
->who
, xfer
->message
, 0, time(NULL
));
521 if ((thumb
= purple_xfer_get_thumbnail(xfer
, &thumb_size
))) {
522 purple_request_accept_cancel_with_icon(xfer
, NULL
, buf
, NULL
,
523 PURPLE_DEFAULT_ACTION_NONE
, xfer
->account
, xfer
->who
, NULL
,
524 thumb
, thumb_size
, xfer
,
525 G_CALLBACK(purple_xfer_choose_file
),
526 G_CALLBACK(cancel_recv_cb
));
528 purple_request_accept_cancel(xfer
, NULL
, buf
, NULL
,
529 PURPLE_DEFAULT_ACTION_NONE
, xfer
->account
, xfer
->who
, NULL
,
530 xfer
, G_CALLBACK(purple_xfer_choose_file
),
531 G_CALLBACK(cancel_recv_cb
));
536 purple_xfer_choose_file(xfer
);
540 ask_accept_ok(PurpleXfer
*xfer
)
542 purple_xfer_request_accepted(xfer
, NULL
);
548 ask_accept_cancel(PurpleXfer
*xfer
)
550 purple_xfer_request_denied(xfer
);
551 purple_xfer_unref(xfer
);
557 purple_xfer_ask_accept(PurpleXfer
*xfer
)
559 char *buf
, *buf2
= NULL
;
560 PurpleBuddy
*buddy
= purple_find_buddy(xfer
->account
, xfer
->who
);
562 buf
= g_strdup_printf(_("Accept file transfer request from %s?"),
563 buddy
? purple_buddy_get_alias(buddy
) : xfer
->who
);
564 if (purple_xfer_get_remote_ip(xfer
) &&
565 purple_xfer_get_remote_port(xfer
))
566 buf2
= g_strdup_printf(_("A file is available for download from:\n"
567 "Remote host: %s\nRemote port: %d"),
568 purple_xfer_get_remote_ip(xfer
),
569 purple_xfer_get_remote_port(xfer
));
570 purple_request_accept_cancel(xfer
, NULL
, buf
, buf2
,
571 PURPLE_DEFAULT_ACTION_NONE
,
572 xfer
->account
, xfer
->who
, NULL
,
574 G_CALLBACK(ask_accept_ok
),
575 G_CALLBACK(ask_accept_cancel
));
581 purple_xfer_request(PurpleXfer
*xfer
)
583 g_return_if_fail(xfer
!= NULL
);
584 g_return_if_fail(xfer
->ops
.init
!= NULL
);
586 purple_xfer_ref(xfer
);
588 if (purple_xfer_get_type(xfer
) == PURPLE_XFER_RECEIVE
)
590 purple_signal_emit(purple_xfers_get_handle(), "file-recv-request", xfer
);
591 if (purple_xfer_get_status(xfer
) == PURPLE_XFER_STATUS_CANCEL_LOCAL
)
593 /* The file-transfer was cancelled by a plugin */
594 purple_xfer_cancel_local(xfer
);
596 else if (purple_xfer_get_filename(xfer
) ||
597 purple_xfer_get_status(xfer
) == PURPLE_XFER_STATUS_ACCEPTED
)
599 gchar
* message
= NULL
;
600 PurpleBuddy
*buddy
= purple_find_buddy(xfer
->account
, xfer
->who
);
602 message
= g_strdup_printf(_("%s is offering to send file %s"),
603 buddy
? purple_buddy_get_alias(buddy
) : xfer
->who
, purple_xfer_get_filename(xfer
));
604 purple_xfer_conversation_write_with_thumbnail(xfer
, message
);
607 /* Ask for a filename to save to if it's not already given by a plugin */
608 if (xfer
->local_filename
== NULL
)
609 purple_xfer_ask_recv(xfer
);
613 purple_xfer_ask_accept(xfer
);
618 purple_xfer_choose_file(xfer
);
623 purple_xfer_request_accepted(PurpleXfer
*xfer
, const char *filename
)
627 char *msg
, *utf8
, *base
;
628 PurpleAccount
*account
;
634 type
= purple_xfer_get_type(xfer
);
635 account
= purple_xfer_get_account(xfer
);
637 purple_debug_misc("xfer", "request accepted for %p\n", xfer
);
639 if (!filename
&& type
== PURPLE_XFER_RECEIVE
) {
640 xfer
->status
= PURPLE_XFER_STATUS_ACCEPTED
;
641 xfer
->ops
.init(xfer
);
644 g_return_if_fail(filename
!= NULL
);
647 buddy
= purple_find_buddy(account
, xfer
->who
);
649 if (type
== PURPLE_XFER_SEND
) {
651 /* Check the filename. */
652 PurpleXferUiOps
*ui_ops
;
653 ui_ops
= purple_xfer_get_ui_ops(xfer
);
656 if (g_strrstr(filename
, "../") || g_strrstr(filename
, "..\\"))
658 if (g_strrstr(filename
, "../"))
661 utf8
= g_filename_to_utf8(filename
, -1, NULL
, NULL
, NULL
);
663 msg
= g_strdup_printf(_("%s is not a valid filename.\n"), utf8
);
664 purple_xfer_error(type
, account
, xfer
->who
, msg
);
668 purple_xfer_unref(xfer
);
672 if (ui_ops
== NULL
|| (ui_ops
->ui_read
== NULL
&& ui_ops
->ui_write
== NULL
)) {
673 if (g_stat(filename
, &st
) == -1) {
674 purple_xfer_show_file_error(xfer
, filename
);
675 purple_xfer_unref(xfer
);
679 purple_xfer_set_local_filename(xfer
, filename
);
680 purple_xfer_set_size(xfer
, st
.st_size
);
682 purple_xfer_set_local_filename(xfer
, filename
);
685 base
= g_path_get_basename(filename
);
686 utf8
= g_filename_to_utf8(base
, -1, NULL
, NULL
, NULL
);
688 purple_xfer_set_filename(xfer
, utf8
);
690 msg
= g_strdup_printf(_("Offering to send %s to %s"),
691 utf8
, buddy
? purple_buddy_get_alias(buddy
) : xfer
->who
);
693 purple_xfer_conversation_write(xfer
, msg
, FALSE
);
697 /* Receiving a file */
698 xfer
->status
= PURPLE_XFER_STATUS_ACCEPTED
;
699 purple_xfer_set_local_filename(xfer
, filename
);
701 msg
= g_strdup_printf(_("Starting transfer of %s from %s"),
702 xfer
->filename
, buddy
? purple_buddy_get_alias(buddy
) : xfer
->who
);
703 purple_xfer_conversation_write(xfer
, msg
, FALSE
);
707 purple_xfer_add(xfer
);
708 xfer
->ops
.init(xfer
);
713 purple_xfer_request_denied(PurpleXfer
*xfer
)
715 g_return_if_fail(xfer
!= NULL
);
717 purple_debug_misc("xfer", "xfer %p denied\n", xfer
);
719 if (xfer
->ops
.request_denied
!= NULL
)
720 xfer
->ops
.request_denied(xfer
);
722 purple_xfer_unref(xfer
);
726 purple_xfer_get_type(const PurpleXfer
*xfer
)
728 g_return_val_if_fail(xfer
!= NULL
, PURPLE_XFER_UNKNOWN
);
734 purple_xfer_get_account(const PurpleXfer
*xfer
)
736 g_return_val_if_fail(xfer
!= NULL
, NULL
);
738 return xfer
->account
;
742 purple_xfer_get_remote_user(const PurpleXfer
*xfer
)
744 g_return_val_if_fail(xfer
!= NULL
, NULL
);
749 purple_xfer_get_status(const PurpleXfer
*xfer
)
751 g_return_val_if_fail(xfer
!= NULL
, PURPLE_XFER_STATUS_UNKNOWN
);
756 /* FIXME: Rename with cancelled for 3.0.0. */
758 purple_xfer_is_canceled(const PurpleXfer
*xfer
)
760 g_return_val_if_fail(xfer
!= NULL
, TRUE
);
762 if ((purple_xfer_get_status(xfer
) == PURPLE_XFER_STATUS_CANCEL_LOCAL
) ||
763 (purple_xfer_get_status(xfer
) == PURPLE_XFER_STATUS_CANCEL_REMOTE
))
770 purple_xfer_is_completed(const PurpleXfer
*xfer
)
772 g_return_val_if_fail(xfer
!= NULL
, TRUE
);
774 return (purple_xfer_get_status(xfer
) == PURPLE_XFER_STATUS_DONE
);
778 purple_xfer_get_filename(const PurpleXfer
*xfer
)
780 g_return_val_if_fail(xfer
!= NULL
, NULL
);
782 return xfer
->filename
;
786 purple_xfer_get_local_filename(const PurpleXfer
*xfer
)
788 g_return_val_if_fail(xfer
!= NULL
, NULL
);
790 return xfer
->local_filename
;
794 purple_xfer_get_bytes_sent(const PurpleXfer
*xfer
)
796 g_return_val_if_fail(xfer
!= NULL
, 0);
798 return xfer
->bytes_sent
;
802 purple_xfer_get_bytes_remaining(const PurpleXfer
*xfer
)
804 g_return_val_if_fail(xfer
!= NULL
, 0);
806 return xfer
->bytes_remaining
;
810 purple_xfer_get_size(const PurpleXfer
*xfer
)
812 g_return_val_if_fail(xfer
!= NULL
, 0);
818 purple_xfer_get_progress(const PurpleXfer
*xfer
)
820 g_return_val_if_fail(xfer
!= NULL
, 0.0);
822 if (purple_xfer_get_size(xfer
) == 0)
825 return ((double)purple_xfer_get_bytes_sent(xfer
) /
826 (double)purple_xfer_get_size(xfer
));
830 purple_xfer_get_local_port(const PurpleXfer
*xfer
)
832 g_return_val_if_fail(xfer
!= NULL
, -1);
834 return xfer
->local_port
;
838 purple_xfer_get_remote_ip(const PurpleXfer
*xfer
)
840 g_return_val_if_fail(xfer
!= NULL
, NULL
);
842 return xfer
->remote_ip
;
846 purple_xfer_get_remote_port(const PurpleXfer
*xfer
)
848 g_return_val_if_fail(xfer
!= NULL
, -1);
850 return xfer
->remote_port
;
854 purple_xfer_get_start_time(const PurpleXfer
*xfer
)
856 g_return_val_if_fail(xfer
!= NULL
, 0);
858 return xfer
->start_time
;
862 purple_xfer_get_end_time(const PurpleXfer
*xfer
)
864 g_return_val_if_fail(xfer
!= NULL
, 0);
866 return xfer
->end_time
;
870 purple_xfer_set_completed(PurpleXfer
*xfer
, gboolean completed
)
872 PurpleXferUiOps
*ui_ops
;
874 g_return_if_fail(xfer
!= NULL
);
876 if (completed
== TRUE
) {
878 PurpleConversation
*conv
;
880 purple_xfer_set_status(xfer
, PURPLE_XFER_STATUS_DONE
);
882 if (purple_xfer_get_filename(xfer
) != NULL
)
884 char *filename
= g_markup_escape_text(purple_xfer_get_filename(xfer
), -1);
885 if (purple_xfer_get_local_filename(xfer
)
886 && purple_xfer_get_type(xfer
) == PURPLE_XFER_RECEIVE
)
888 char *local
= g_markup_escape_text(purple_xfer_get_local_filename(xfer
), -1);
889 msg
= g_strdup_printf(_("Transfer of file <A HREF=\"file://%s\">%s</A> complete"),
894 msg
= g_strdup_printf(_("Transfer of file %s complete"),
899 msg
= g_strdup(_("File transfer complete"));
901 conv
= purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM
, xfer
->who
,
902 purple_xfer_get_account(xfer
));
905 purple_conversation_write(conv
, NULL
, msg
, PURPLE_MESSAGE_SYSTEM
, time(NULL
));
909 ui_ops
= purple_xfer_get_ui_ops(xfer
);
911 if (ui_ops
!= NULL
&& ui_ops
->update_progress
!= NULL
)
912 ui_ops
->update_progress(xfer
, purple_xfer_get_progress(xfer
));
916 purple_xfer_set_message(PurpleXfer
*xfer
, const char *message
)
918 g_return_if_fail(xfer
!= NULL
);
920 g_free(xfer
->message
);
921 xfer
->message
= g_strdup(message
);
925 purple_xfer_set_filename(PurpleXfer
*xfer
, const char *filename
)
927 g_return_if_fail(xfer
!= NULL
);
929 g_free(xfer
->filename
);
930 xfer
->filename
= g_strdup(filename
);
934 purple_xfer_set_local_filename(PurpleXfer
*xfer
, const char *filename
)
936 g_return_if_fail(xfer
!= NULL
);
938 g_free(xfer
->local_filename
);
939 xfer
->local_filename
= g_strdup(filename
);
943 purple_xfer_set_size(PurpleXfer
*xfer
, size_t size
)
945 g_return_if_fail(xfer
!= NULL
);
948 xfer
->bytes_remaining
= xfer
->size
- purple_xfer_get_bytes_sent(xfer
);
952 purple_xfer_set_bytes_sent(PurpleXfer
*xfer
, size_t bytes_sent
)
954 g_return_if_fail(xfer
!= NULL
);
956 xfer
->bytes_sent
= bytes_sent
;
957 xfer
->bytes_remaining
= purple_xfer_get_size(xfer
) - bytes_sent
;
961 purple_xfer_get_ui_ops(const PurpleXfer
*xfer
)
963 g_return_val_if_fail(xfer
!= NULL
, NULL
);
969 purple_xfer_set_init_fnc(PurpleXfer
*xfer
, void (*fnc
)(PurpleXfer
*))
971 g_return_if_fail(xfer
!= NULL
);
973 xfer
->ops
.init
= fnc
;
976 void purple_xfer_set_request_denied_fnc(PurpleXfer
*xfer
, void (*fnc
)(PurpleXfer
*))
978 g_return_if_fail(xfer
!= NULL
);
980 xfer
->ops
.request_denied
= fnc
;
984 purple_xfer_set_read_fnc(PurpleXfer
*xfer
, gssize (*fnc
)(guchar
**, PurpleXfer
*))
986 g_return_if_fail(xfer
!= NULL
);
988 xfer
->ops
.read
= fnc
;
992 purple_xfer_set_write_fnc(PurpleXfer
*xfer
,
993 gssize (*fnc
)(const guchar
*, size_t, PurpleXfer
*))
995 g_return_if_fail(xfer
!= NULL
);
997 xfer
->ops
.write
= fnc
;
1001 purple_xfer_set_ack_fnc(PurpleXfer
*xfer
,
1002 void (*fnc
)(PurpleXfer
*, const guchar
*, size_t))
1004 g_return_if_fail(xfer
!= NULL
);
1006 xfer
->ops
.ack
= fnc
;
1010 purple_xfer_set_start_fnc(PurpleXfer
*xfer
, void (*fnc
)(PurpleXfer
*))
1012 g_return_if_fail(xfer
!= NULL
);
1014 xfer
->ops
.start
= fnc
;
1018 purple_xfer_set_end_fnc(PurpleXfer
*xfer
, void (*fnc
)(PurpleXfer
*))
1020 g_return_if_fail(xfer
!= NULL
);
1022 xfer
->ops
.end
= fnc
;
1026 purple_xfer_set_cancel_send_fnc(PurpleXfer
*xfer
, void (*fnc
)(PurpleXfer
*))
1028 g_return_if_fail(xfer
!= NULL
);
1030 xfer
->ops
.cancel_send
= fnc
;
1034 purple_xfer_set_cancel_recv_fnc(PurpleXfer
*xfer
, void (*fnc
)(PurpleXfer
*))
1036 g_return_if_fail(xfer
!= NULL
);
1038 xfer
->ops
.cancel_recv
= fnc
;
1042 purple_xfer_increase_buffer_size(PurpleXfer
*xfer
)
1044 xfer
->current_buffer_size
= MIN(xfer
->current_buffer_size
* 1.5,
1045 FT_MAX_BUFFER_SIZE
);
1049 purple_xfer_read(PurpleXfer
*xfer
, guchar
**buffer
)
1053 g_return_val_if_fail(xfer
!= NULL
, 0);
1054 g_return_val_if_fail(buffer
!= NULL
, 0);
1056 if (purple_xfer_get_size(xfer
) == 0)
1057 s
= xfer
->current_buffer_size
;
1059 s
= MIN(purple_xfer_get_bytes_remaining(xfer
), xfer
->current_buffer_size
);
1061 if (xfer
->ops
.read
!= NULL
) {
1062 r
= (xfer
->ops
.read
)(buffer
, xfer
);
1065 *buffer
= g_malloc0(s
);
1067 r
= read(xfer
->fd
, *buffer
, s
);
1068 if (r
< 0 && errno
== EAGAIN
)
1076 if (r
>= 0 && (gsize
)r
== xfer
->current_buffer_size
)
1078 * We managed to read the entire buffer. This means our this
1079 * network is fast and our buffer is too small, so make it
1082 purple_xfer_increase_buffer_size(xfer
);
1088 purple_xfer_write(PurpleXfer
*xfer
, const guchar
*buffer
, gsize size
)
1092 g_return_val_if_fail(xfer
!= NULL
, 0);
1093 g_return_val_if_fail(buffer
!= NULL
, 0);
1094 g_return_val_if_fail(size
!= 0, 0);
1096 s
= MIN(purple_xfer_get_bytes_remaining(xfer
), size
);
1098 if (xfer
->ops
.write
!= NULL
) {
1099 r
= (xfer
->ops
.write
)(buffer
, s
, xfer
);
1101 r
= write(xfer
->fd
, buffer
, s
);
1102 if (r
< 0 && errno
== EAGAIN
)
1105 if (r
>= 0 && (purple_xfer_get_bytes_sent(xfer
)+r
) >= purple_xfer_get_size(xfer
) &&
1106 !purple_xfer_is_completed(xfer
))
1107 purple_xfer_set_completed(xfer
, TRUE
);
1113 purple_xfer_write_file(PurpleXfer
*xfer
, const guchar
*buffer
, gsize size
)
1115 PurpleXferUiOps
*ui_ops
;
1119 g_return_val_if_fail(buffer
!= NULL
, FALSE
);
1121 ui_ops
= purple_xfer_get_ui_ops(xfer
);
1122 fs_known
= (purple_xfer_get_size(xfer
) > 0);
1124 if (fs_known
&& size
> purple_xfer_get_bytes_remaining(xfer
)) {
1125 purple_debug_warning("xfer",
1126 "Got too much data (truncating at %" G_GSIZE_FORMAT
1127 ").\n", purple_xfer_get_size(xfer
));
1128 size
= purple_xfer_get_bytes_remaining(xfer
);
1131 if (ui_ops
&& ui_ops
->ui_write
)
1132 wc
= ui_ops
->ui_write(xfer
, buffer
, size
);
1134 if (xfer
->dest_fp
== NULL
) {
1135 purple_debug_error("xfer",
1136 "File is not opened for writing\n");
1137 purple_xfer_cancel_local(xfer
);
1140 wc
= fwrite(buffer
, 1, size
, xfer
->dest_fp
);
1144 purple_debug_error("xfer",
1145 "Unable to write whole buffer.\n");
1146 purple_xfer_cancel_local(xfer
);
1150 purple_xfer_set_bytes_sent(xfer
, purple_xfer_get_bytes_sent(xfer
) +
1157 purple_xfer_read_file(PurpleXfer
*xfer
, guchar
*buffer
, gsize size
)
1159 PurpleXferUiOps
*ui_ops
;
1162 g_return_val_if_fail(buffer
!= NULL
, FALSE
);
1164 ui_ops
= purple_xfer_get_ui_ops(xfer
);
1166 if (ui_ops
&& ui_ops
->ui_read
) {
1167 guchar
*buffer_got
= NULL
;
1169 got_len
= ui_ops
->ui_read(xfer
, &buffer_got
, size
);
1171 if (got_len
>= 0 && (gsize
)got_len
> size
) {
1173 purple_debug_error("xfer",
1174 "Got too much data from UI.\n");
1175 purple_xfer_cancel_local(xfer
);
1180 memcpy(buffer
, buffer_got
, got_len
);
1183 if (xfer
->dest_fp
== NULL
) {
1184 purple_debug_error("xfer",
1185 "File is not opened for reading\n");
1186 purple_xfer_cancel_local(xfer
);
1189 got_len
= fread(buffer
, 1, size
, xfer
->dest_fp
);
1190 if ((got_len
< 0 || (gsize
)got_len
!= size
) &&
1191 ferror(xfer
->dest_fp
))
1193 purple_debug_error("xfer",
1194 "Unable to read file.\n");
1195 purple_xfer_cancel_local(xfer
);
1201 purple_xfer_set_bytes_sent(xfer
,
1202 purple_xfer_get_bytes_sent(xfer
) + got_len
);
1209 do_transfer(PurpleXfer
*xfer
)
1211 PurpleXferUiOps
*ui_ops
;
1212 guchar
*buffer
= NULL
;
1215 ui_ops
= purple_xfer_get_ui_ops(xfer
);
1217 if (xfer
->type
== PURPLE_XFER_RECEIVE
) {
1218 r
= purple_xfer_read(xfer
, &buffer
);
1221 if (ui_ops
&& ui_ops
->ui_write
)
1222 wc
= ui_ops
->ui_write(xfer
, buffer
, r
);
1224 wc
= fwrite(buffer
, 1, r
, xfer
->dest_fp
);
1226 if (wc
!= (gsize
)r
) {
1227 purple_debug_error("filetransfer", "Unable to write whole buffer.\n");
1228 purple_xfer_cancel_local(xfer
);
1233 if ((purple_xfer_get_size(xfer
) > 0) &&
1234 ((purple_xfer_get_bytes_sent(xfer
)+r
) >= purple_xfer_get_size(xfer
)))
1235 purple_xfer_set_completed(xfer
, TRUE
);
1237 purple_xfer_cancel_remote(xfer
);
1241 } else if (xfer
->type
== PURPLE_XFER_SEND
) {
1243 size_t s
= MIN(purple_xfer_get_bytes_remaining(xfer
), xfer
->current_buffer_size
);
1244 PurpleXferPrivData
*priv
= g_hash_table_lookup(xfers_data
, xfer
);
1245 gboolean read
= TRUE
;
1247 /* this is so the prpl can keep the connection open
1248 if it needs to for some odd reason. */
1250 if (xfer
->watcher
) {
1251 purple_input_remove(xfer
->watcher
);
1258 if (priv
->buffer
->len
< s
) {
1259 s
-= priv
->buffer
->len
;
1267 if (ui_ops
&& ui_ops
->ui_read
) {
1268 gssize tmp
= ui_ops
->ui_read(xfer
, &buffer
, s
);
1271 * The UI claimed it was ready, but didn't have any data for
1272 * us... It will call purple_xfer_ui_ready when ready, which
1273 * sets back up this watcher.
1275 if (xfer
->watcher
!= 0) {
1276 purple_input_remove(xfer
->watcher
);
1280 /* Need to indicate the prpl is still ready... */
1281 priv
->ready
|= PURPLE_XFER_READY_PRPL
;
1283 g_return_if_reached();
1284 } else if (tmp
< 0) {
1285 purple_debug_error("filetransfer", "Unable to read whole buffer.\n");
1286 purple_xfer_cancel_local(xfer
);
1292 buffer
= g_malloc(s
);
1293 result
= fread(buffer
, 1, s
, xfer
->dest_fp
);
1295 purple_debug_error("filetransfer", "Unable to read whole buffer.\n");
1296 purple_xfer_cancel_local(xfer
);
1304 g_byte_array_append(priv
->buffer
, buffer
, result
);
1306 buffer
= priv
->buffer
->data
;
1307 result
= priv
->buffer
->len
;
1310 r
= purple_xfer_write(xfer
, buffer
, result
);
1313 purple_xfer_cancel_remote(xfer
);
1315 /* We don't free buffer if priv->buffer is set, because in
1316 that case buffer doesn't belong to us. */
1319 } else if (r
>= 0 && (gsize
)r
== result
) {
1321 * We managed to write the entire buffer. This means our
1322 * network is fast and our buffer is too small, so make it
1325 purple_xfer_increase_buffer_size(xfer
);
1327 if (ui_ops
&& ui_ops
->data_not_sent
)
1328 ui_ops
->data_not_sent(xfer
, buffer
+ r
, result
- r
);
1333 * Remove what we wrote
1334 * If we wrote the whole buffer the byte array will be empty
1335 * Otherwise we'll keep what wasn't sent for next time.
1338 g_byte_array_remove_range(priv
->buffer
, 0, r
);
1343 if (purple_xfer_get_size(xfer
) > 0)
1344 xfer
->bytes_remaining
-= r
;
1346 xfer
->bytes_sent
+= r
;
1348 if (xfer
->ops
.ack
!= NULL
)
1349 xfer
->ops
.ack(xfer
, buffer
, r
);
1353 if (ui_ops
!= NULL
&& ui_ops
->update_progress
!= NULL
)
1354 ui_ops
->update_progress(xfer
,
1355 purple_xfer_get_progress(xfer
));
1358 if (purple_xfer_is_completed(xfer
))
1359 purple_xfer_end(xfer
);
1363 transfer_cb(gpointer data
, gint source
, PurpleInputCondition condition
)
1365 PurpleXfer
*xfer
= data
;
1367 if (xfer
->dest_fp
== NULL
) {
1368 /* The UI is moderating its side manually */
1369 PurpleXferPrivData
*priv
= g_hash_table_lookup(xfers_data
, xfer
);
1370 if (0 == (priv
->ready
& PURPLE_XFER_READY_UI
)) {
1371 priv
->ready
|= PURPLE_XFER_READY_PRPL
;
1373 purple_input_remove(xfer
->watcher
);
1376 purple_debug_misc("xfer", "prpl is ready on ft %p, waiting for UI\n", xfer
);
1380 priv
->ready
= PURPLE_XFER_READY_NONE
;
1387 begin_transfer(PurpleXfer
*xfer
, PurpleInputCondition cond
)
1389 PurpleXferType type
= purple_xfer_get_type(xfer
);
1390 PurpleXferUiOps
*ui_ops
= purple_xfer_get_ui_ops(xfer
);
1392 if (xfer
->start_time
!= 0) {
1393 purple_debug_error("xfer", "Transfer is being started multiple times\n");
1394 g_return_if_reached();
1397 if (ui_ops
== NULL
|| (ui_ops
->ui_read
== NULL
&& ui_ops
->ui_write
== NULL
)) {
1398 xfer
->dest_fp
= g_fopen(purple_xfer_get_local_filename(xfer
),
1399 type
== PURPLE_XFER_RECEIVE
? "wb" : "rb");
1401 if (xfer
->dest_fp
== NULL
) {
1402 purple_xfer_show_file_error(xfer
, purple_xfer_get_local_filename(xfer
));
1403 purple_xfer_cancel_local(xfer
);
1407 if (fseek(xfer
->dest_fp
, xfer
->bytes_sent
, SEEK_SET
) != 0) {
1408 purple_debug_error("xfer", "couldn't seek\n");
1409 purple_xfer_show_file_error(xfer
, purple_xfer_get_local_filename(xfer
));
1410 purple_xfer_cancel_local(xfer
);
1416 xfer
->watcher
= purple_input_add(xfer
->fd
, cond
, transfer_cb
, xfer
);
1418 xfer
->start_time
= time(NULL
);
1420 if (xfer
->ops
.start
!= NULL
)
1421 xfer
->ops
.start(xfer
);
1425 connect_cb(gpointer data
, gint source
, const gchar
*error_message
)
1427 PurpleXfer
*xfer
= (PurpleXfer
*)data
;
1430 purple_xfer_cancel_local(xfer
);
1436 begin_transfer(xfer
, PURPLE_INPUT_READ
);
1440 purple_xfer_ui_ready(PurpleXfer
*xfer
)
1442 PurpleInputCondition cond
;
1443 PurpleXferType type
;
1444 PurpleXferPrivData
*priv
;
1446 g_return_if_fail(xfer
!= NULL
);
1448 priv
= g_hash_table_lookup(xfers_data
, xfer
);
1449 priv
->ready
|= PURPLE_XFER_READY_UI
;
1451 if (0 == (priv
->ready
& PURPLE_XFER_READY_PRPL
)) {
1452 purple_debug_misc("xfer", "UI is ready on ft %p, waiting for prpl\n", xfer
);
1456 purple_debug_misc("xfer", "UI (and prpl) ready on ft %p, so proceeding\n", xfer
);
1458 type
= purple_xfer_get_type(xfer
);
1459 if (type
== PURPLE_XFER_SEND
)
1460 cond
= PURPLE_INPUT_WRITE
;
1461 else /* if (type == PURPLE_XFER_RECEIVE) */
1462 cond
= PURPLE_INPUT_READ
;
1464 if (xfer
->watcher
== 0 && xfer
->fd
!= -1)
1465 xfer
->watcher
= purple_input_add(xfer
->fd
, cond
, transfer_cb
, xfer
);
1467 priv
->ready
= PURPLE_XFER_READY_NONE
;
1473 purple_xfer_prpl_ready(PurpleXfer
*xfer
)
1475 PurpleXferPrivData
*priv
;
1477 g_return_if_fail(xfer
!= NULL
);
1479 priv
= g_hash_table_lookup(xfers_data
, xfer
);
1480 priv
->ready
|= PURPLE_XFER_READY_PRPL
;
1482 /* I don't think fwrite/fread are ever *not* ready */
1483 if (xfer
->dest_fp
== NULL
&& 0 == (priv
->ready
& PURPLE_XFER_READY_UI
)) {
1484 purple_debug_misc("xfer", "prpl is ready on ft %p, waiting for UI\n", xfer
);
1488 purple_debug_misc("xfer", "Prpl (and UI) ready on ft %p, so proceeding\n", xfer
);
1490 priv
->ready
= PURPLE_XFER_READY_NONE
;
1496 purple_xfer_start(PurpleXfer
*xfer
, int fd
, const char *ip
,
1499 PurpleInputCondition cond
;
1500 PurpleXferType type
;
1502 g_return_if_fail(xfer
!= NULL
);
1503 g_return_if_fail(purple_xfer_get_type(xfer
) != PURPLE_XFER_UNKNOWN
);
1505 type
= purple_xfer_get_type(xfer
);
1507 purple_xfer_set_status(xfer
, PURPLE_XFER_STATUS_STARTED
);
1510 * FIXME 3.0.0 -- there's too much broken code depending on fd == 0
1511 * meaning "don't use a real fd"
1516 if (type
== PURPLE_XFER_RECEIVE
) {
1517 cond
= PURPLE_INPUT_READ
;
1520 xfer
->remote_ip
= g_strdup(ip
);
1521 xfer
->remote_port
= port
;
1523 /* Establish a file descriptor. */
1524 purple_proxy_connect(NULL
, xfer
->account
, xfer
->remote_ip
,
1525 xfer
->remote_port
, connect_cb
, xfer
);
1534 cond
= PURPLE_INPUT_WRITE
;
1539 begin_transfer(xfer
, cond
);
1543 purple_xfer_end(PurpleXfer
*xfer
)
1545 g_return_if_fail(xfer
!= NULL
);
1547 /* See if we are actually trying to cancel this. */
1548 if (!purple_xfer_is_completed(xfer
)) {
1549 purple_xfer_cancel_local(xfer
);
1553 xfer
->end_time
= time(NULL
);
1554 if (xfer
->ops
.end
!= NULL
)
1555 xfer
->ops
.end(xfer
);
1557 if (xfer
->watcher
!= 0) {
1558 purple_input_remove(xfer
->watcher
);
1565 if (xfer
->dest_fp
!= NULL
) {
1566 fclose(xfer
->dest_fp
);
1567 xfer
->dest_fp
= NULL
;
1570 purple_xfer_unref(xfer
);
1574 purple_xfer_add(PurpleXfer
*xfer
)
1576 PurpleXferUiOps
*ui_ops
;
1578 g_return_if_fail(xfer
!= NULL
);
1580 ui_ops
= purple_xfer_get_ui_ops(xfer
);
1582 if (ui_ops
!= NULL
&& ui_ops
->add_xfer
!= NULL
)
1583 ui_ops
->add_xfer(xfer
);
1587 purple_xfer_cancel_local(PurpleXfer
*xfer
)
1589 PurpleXferUiOps
*ui_ops
;
1592 g_return_if_fail(xfer
!= NULL
);
1594 /* TODO: We definitely want to close any open request dialogs associated
1595 with this transfer. However, in some cases the request dialog might
1596 own a reference on the xfer. This happens at least with the "%s wants
1597 to send you %s" dialog from purple_xfer_ask_recv(). In these cases
1598 the ref count will not be decremented when the request dialog is
1599 closed, so the ref count will never reach 0 and the xfer will never
1600 be freed. This is a memleak and should be fixed. It's not clear what
1601 the correct fix is. Probably requests should have a destroy function
1602 that is called when the request is destroyed. But also, ref counting
1603 xfer objects makes this code REALLY complicated. An alternate fix is
1604 to not ref count and instead just make sure the object still exists
1605 when we try to use it. */
1606 purple_request_close_with_handle(xfer
);
1608 purple_xfer_set_status(xfer
, PURPLE_XFER_STATUS_CANCEL_LOCAL
);
1609 xfer
->end_time
= time(NULL
);
1611 if (purple_xfer_get_filename(xfer
) != NULL
)
1613 msg
= g_strdup_printf(_("You cancelled the transfer of %s"),
1614 purple_xfer_get_filename(xfer
));
1618 msg
= g_strdup(_("File transfer cancelled"));
1620 purple_xfer_conversation_write(xfer
, msg
, FALSE
);
1623 if (purple_xfer_get_type(xfer
) == PURPLE_XFER_SEND
)
1625 if (xfer
->ops
.cancel_send
!= NULL
)
1626 xfer
->ops
.cancel_send(xfer
);
1630 if (xfer
->ops
.cancel_recv
!= NULL
)
1631 xfer
->ops
.cancel_recv(xfer
);
1634 if (xfer
->watcher
!= 0) {
1635 purple_input_remove(xfer
->watcher
);
1642 if (xfer
->dest_fp
!= NULL
) {
1643 fclose(xfer
->dest_fp
);
1644 xfer
->dest_fp
= NULL
;
1647 ui_ops
= purple_xfer_get_ui_ops(xfer
);
1649 if (ui_ops
!= NULL
&& ui_ops
->cancel_local
!= NULL
)
1650 ui_ops
->cancel_local(xfer
);
1652 xfer
->bytes_remaining
= 0;
1654 purple_xfer_unref(xfer
);
1658 purple_xfer_cancel_remote(PurpleXfer
*xfer
)
1660 PurpleXferUiOps
*ui_ops
;
1662 PurpleAccount
*account
;
1665 g_return_if_fail(xfer
!= NULL
);
1667 purple_request_close_with_handle(xfer
);
1668 purple_xfer_set_status(xfer
, PURPLE_XFER_STATUS_CANCEL_REMOTE
);
1669 xfer
->end_time
= time(NULL
);
1671 account
= purple_xfer_get_account(xfer
);
1672 buddy
= purple_find_buddy(account
, xfer
->who
);
1674 if (purple_xfer_get_filename(xfer
) != NULL
)
1676 msg
= g_strdup_printf(_("%s cancelled the transfer of %s"),
1677 buddy
? purple_buddy_get_alias(buddy
) : xfer
->who
, purple_xfer_get_filename(xfer
));
1681 msg
= g_strdup_printf(_("%s cancelled the file transfer"),
1682 buddy
? purple_buddy_get_alias(buddy
) : xfer
->who
);
1684 purple_xfer_conversation_write(xfer
, msg
, TRUE
);
1685 purple_xfer_error(purple_xfer_get_type(xfer
), account
, xfer
->who
, msg
);
1688 if (purple_xfer_get_type(xfer
) == PURPLE_XFER_SEND
)
1690 if (xfer
->ops
.cancel_send
!= NULL
)
1691 xfer
->ops
.cancel_send(xfer
);
1695 if (xfer
->ops
.cancel_recv
!= NULL
)
1696 xfer
->ops
.cancel_recv(xfer
);
1699 if (xfer
->watcher
!= 0) {
1700 purple_input_remove(xfer
->watcher
);
1707 if (xfer
->dest_fp
!= NULL
) {
1708 fclose(xfer
->dest_fp
);
1709 xfer
->dest_fp
= NULL
;
1712 ui_ops
= purple_xfer_get_ui_ops(xfer
);
1714 if (ui_ops
!= NULL
&& ui_ops
->cancel_remote
!= NULL
)
1715 ui_ops
->cancel_remote(xfer
);
1717 xfer
->bytes_remaining
= 0;
1719 purple_xfer_unref(xfer
);
1723 purple_xfer_error(PurpleXferType type
, PurpleAccount
*account
, const char *who
, const char *msg
)
1727 g_return_if_fail(msg
!= NULL
);
1728 g_return_if_fail(type
!= PURPLE_XFER_UNKNOWN
);
1732 buddy
= purple_find_buddy(account
, who
);
1734 who
= purple_buddy_get_alias(buddy
);
1737 if (type
== PURPLE_XFER_SEND
)
1738 title
= g_strdup_printf(_("File transfer to %s failed."), who
);
1740 title
= g_strdup_printf(_("File transfer from %s failed."), who
);
1742 purple_notify_error(NULL
, NULL
, title
, msg
);
1748 purple_xfer_update_progress(PurpleXfer
*xfer
)
1750 PurpleXferUiOps
*ui_ops
;
1752 g_return_if_fail(xfer
!= NULL
);
1754 ui_ops
= purple_xfer_get_ui_ops(xfer
);
1755 if (ui_ops
!= NULL
&& ui_ops
->update_progress
!= NULL
)
1756 ui_ops
->update_progress(xfer
, purple_xfer_get_progress(xfer
));
1760 purple_xfer_get_thumbnail(const PurpleXfer
*xfer
, gsize
*len
)
1762 PurpleXferPrivData
*priv
= g_hash_table_lookup(xfers_data
, xfer
);
1765 *len
= priv
->thumbnail_size
;
1767 return priv
->thumbnail_data
;
1771 purple_xfer_get_thumbnail_mimetype(const PurpleXfer
*xfer
)
1773 PurpleXferPrivData
*priv
= g_hash_table_lookup(xfers_data
, xfer
);
1775 return priv
->thumbnail_mimetype
;
1779 purple_xfer_set_thumbnail(PurpleXfer
*xfer
, gconstpointer thumbnail
,
1780 gsize size
, const gchar
*mimetype
)
1782 PurpleXferPrivData
*priv
= g_hash_table_lookup(xfers_data
, xfer
);
1784 g_free(priv
->thumbnail_data
);
1785 g_free(priv
->thumbnail_mimetype
);
1787 if (thumbnail
&& size
> 0) {
1788 priv
->thumbnail_data
= g_memdup(thumbnail
, size
);
1789 priv
->thumbnail_size
= size
;
1790 priv
->thumbnail_mimetype
= g_strdup(mimetype
);
1792 priv
->thumbnail_data
= NULL
;
1793 priv
->thumbnail_size
= 0;
1794 priv
->thumbnail_mimetype
= NULL
;
1799 purple_xfer_prepare_thumbnail(PurpleXfer
*xfer
, const gchar
*formats
)
1801 if (xfer
->ui_ops
->add_thumbnail
) {
1802 xfer
->ui_ops
->add_thumbnail(xfer
, formats
);
1806 /**************************************************************************
1807 * File Transfer Subsystem API
1808 **************************************************************************/
1810 purple_xfers_get_handle(void) {
1811 static int handle
= 0;
1817 purple_xfers_init(void) {
1818 void *handle
= purple_xfers_get_handle();
1820 xfers_data
= g_hash_table_new_full(g_direct_hash
, g_direct_equal
,
1821 NULL
, purple_xfer_priv_data_destroy
);
1823 /* register signals */
1824 purple_signal_register(handle
, "file-recv-accept",
1825 purple_marshal_VOID__POINTER
, NULL
, 1,
1826 purple_value_new(PURPLE_TYPE_SUBTYPE
,
1827 PURPLE_SUBTYPE_XFER
));
1828 purple_signal_register(handle
, "file-send-accept",
1829 purple_marshal_VOID__POINTER
, NULL
, 1,
1830 purple_value_new(PURPLE_TYPE_SUBTYPE
,
1831 PURPLE_SUBTYPE_XFER
));
1832 purple_signal_register(handle
, "file-recv-start",
1833 purple_marshal_VOID__POINTER
, NULL
, 1,
1834 purple_value_new(PURPLE_TYPE_SUBTYPE
,
1835 PURPLE_SUBTYPE_XFER
));
1836 purple_signal_register(handle
, "file-send-start",
1837 purple_marshal_VOID__POINTER
, NULL
, 1,
1838 purple_value_new(PURPLE_TYPE_SUBTYPE
,
1839 PURPLE_SUBTYPE_XFER
));
1840 purple_signal_register(handle
, "file-send-cancel",
1841 purple_marshal_VOID__POINTER
, NULL
, 1,
1842 purple_value_new(PURPLE_TYPE_SUBTYPE
,
1843 PURPLE_SUBTYPE_XFER
));
1844 purple_signal_register(handle
, "file-recv-cancel",
1845 purple_marshal_VOID__POINTER
, NULL
, 1,
1846 purple_value_new(PURPLE_TYPE_SUBTYPE
,
1847 PURPLE_SUBTYPE_XFER
));
1848 purple_signal_register(handle
, "file-send-complete",
1849 purple_marshal_VOID__POINTER
, NULL
, 1,
1850 purple_value_new(PURPLE_TYPE_SUBTYPE
,
1851 PURPLE_SUBTYPE_XFER
));
1852 purple_signal_register(handle
, "file-recv-complete",
1853 purple_marshal_VOID__POINTER
, NULL
, 1,
1854 purple_value_new(PURPLE_TYPE_SUBTYPE
,
1855 PURPLE_SUBTYPE_XFER
));
1856 purple_signal_register(handle
, "file-recv-request",
1857 purple_marshal_VOID__POINTER
, NULL
, 1,
1858 purple_value_new(PURPLE_TYPE_SUBTYPE
,
1859 PURPLE_SUBTYPE_XFER
));
1863 purple_xfers_uninit(void)
1865 void *handle
= purple_xfers_get_handle();
1867 purple_signals_disconnect_by_handle(handle
);
1868 purple_signals_unregister_by_instance(handle
);
1870 g_hash_table_destroy(xfers_data
);
1875 purple_xfers_set_ui_ops(PurpleXferUiOps
*ops
) {
1880 purple_xfers_get_ui_ops(void) {