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
);
645 buddy
= purple_find_buddy(account
, xfer
->who
);
647 if (type
== PURPLE_XFER_SEND
) {
649 /* Check the filename. */
650 PurpleXferUiOps
*ui_ops
;
651 ui_ops
= purple_xfer_get_ui_ops(xfer
);
654 if (g_strrstr(filename
, "../") || g_strrstr(filename
, "..\\"))
656 if (g_strrstr(filename
, "../"))
659 utf8
= g_filename_to_utf8(filename
, -1, NULL
, NULL
, NULL
);
661 msg
= g_strdup_printf(_("%s is not a valid filename.\n"), utf8
);
662 purple_xfer_error(type
, account
, xfer
->who
, msg
);
666 purple_xfer_unref(xfer
);
670 if (ui_ops
== NULL
|| (ui_ops
->ui_read
== NULL
&& ui_ops
->ui_write
== NULL
)) {
671 if (g_stat(filename
, &st
) == -1) {
672 purple_xfer_show_file_error(xfer
, filename
);
673 purple_xfer_unref(xfer
);
677 purple_xfer_set_local_filename(xfer
, filename
);
678 purple_xfer_set_size(xfer
, st
.st_size
);
680 purple_xfer_set_local_filename(xfer
, filename
);
683 base
= g_path_get_basename(filename
);
684 utf8
= g_filename_to_utf8(base
, -1, NULL
, NULL
, NULL
);
686 purple_xfer_set_filename(xfer
, utf8
);
688 msg
= g_strdup_printf(_("Offering to send %s to %s"),
689 utf8
, buddy
? purple_buddy_get_alias(buddy
) : xfer
->who
);
691 purple_xfer_conversation_write(xfer
, msg
, FALSE
);
695 /* Receiving a file */
696 xfer
->status
= PURPLE_XFER_STATUS_ACCEPTED
;
697 purple_xfer_set_local_filename(xfer
, filename
);
699 msg
= g_strdup_printf(_("Starting transfer of %s from %s"),
700 xfer
->filename
, buddy
? purple_buddy_get_alias(buddy
) : xfer
->who
);
701 purple_xfer_conversation_write(xfer
, msg
, FALSE
);
705 purple_xfer_add(xfer
);
706 xfer
->ops
.init(xfer
);
711 purple_xfer_request_denied(PurpleXfer
*xfer
)
713 g_return_if_fail(xfer
!= NULL
);
715 purple_debug_misc("xfer", "xfer %p denied\n", xfer
);
717 if (xfer
->ops
.request_denied
!= NULL
)
718 xfer
->ops
.request_denied(xfer
);
720 purple_xfer_unref(xfer
);
724 purple_xfer_get_type(const PurpleXfer
*xfer
)
726 g_return_val_if_fail(xfer
!= NULL
, PURPLE_XFER_UNKNOWN
);
732 purple_xfer_get_account(const PurpleXfer
*xfer
)
734 g_return_val_if_fail(xfer
!= NULL
, NULL
);
736 return xfer
->account
;
740 purple_xfer_get_remote_user(const PurpleXfer
*xfer
)
742 g_return_val_if_fail(xfer
!= NULL
, NULL
);
747 purple_xfer_get_status(const PurpleXfer
*xfer
)
749 g_return_val_if_fail(xfer
!= NULL
, PURPLE_XFER_STATUS_UNKNOWN
);
754 /* FIXME: Rename with cancelled for 3.0.0. */
756 purple_xfer_is_canceled(const PurpleXfer
*xfer
)
758 g_return_val_if_fail(xfer
!= NULL
, TRUE
);
760 if ((purple_xfer_get_status(xfer
) == PURPLE_XFER_STATUS_CANCEL_LOCAL
) ||
761 (purple_xfer_get_status(xfer
) == PURPLE_XFER_STATUS_CANCEL_REMOTE
))
768 purple_xfer_is_completed(const PurpleXfer
*xfer
)
770 g_return_val_if_fail(xfer
!= NULL
, TRUE
);
772 return (purple_xfer_get_status(xfer
) == PURPLE_XFER_STATUS_DONE
);
776 purple_xfer_get_filename(const PurpleXfer
*xfer
)
778 g_return_val_if_fail(xfer
!= NULL
, NULL
);
780 return xfer
->filename
;
784 purple_xfer_get_local_filename(const PurpleXfer
*xfer
)
786 g_return_val_if_fail(xfer
!= NULL
, NULL
);
788 return xfer
->local_filename
;
792 purple_xfer_get_bytes_sent(const PurpleXfer
*xfer
)
794 g_return_val_if_fail(xfer
!= NULL
, 0);
796 return xfer
->bytes_sent
;
800 purple_xfer_get_bytes_remaining(const PurpleXfer
*xfer
)
802 g_return_val_if_fail(xfer
!= NULL
, 0);
804 return xfer
->bytes_remaining
;
808 purple_xfer_get_size(const PurpleXfer
*xfer
)
810 g_return_val_if_fail(xfer
!= NULL
, 0);
816 purple_xfer_get_progress(const PurpleXfer
*xfer
)
818 g_return_val_if_fail(xfer
!= NULL
, 0.0);
820 if (purple_xfer_get_size(xfer
) == 0)
823 return ((double)purple_xfer_get_bytes_sent(xfer
) /
824 (double)purple_xfer_get_size(xfer
));
828 purple_xfer_get_local_port(const PurpleXfer
*xfer
)
830 g_return_val_if_fail(xfer
!= NULL
, -1);
832 return xfer
->local_port
;
836 purple_xfer_get_remote_ip(const PurpleXfer
*xfer
)
838 g_return_val_if_fail(xfer
!= NULL
, NULL
);
840 return xfer
->remote_ip
;
844 purple_xfer_get_remote_port(const PurpleXfer
*xfer
)
846 g_return_val_if_fail(xfer
!= NULL
, -1);
848 return xfer
->remote_port
;
852 purple_xfer_get_start_time(const PurpleXfer
*xfer
)
854 g_return_val_if_fail(xfer
!= NULL
, 0);
856 return xfer
->start_time
;
860 purple_xfer_get_end_time(const PurpleXfer
*xfer
)
862 g_return_val_if_fail(xfer
!= NULL
, 0);
864 return xfer
->end_time
;
868 purple_xfer_set_completed(PurpleXfer
*xfer
, gboolean completed
)
870 PurpleXferUiOps
*ui_ops
;
872 g_return_if_fail(xfer
!= NULL
);
874 if (completed
== TRUE
) {
876 PurpleConversation
*conv
;
878 purple_xfer_set_status(xfer
, PURPLE_XFER_STATUS_DONE
);
880 if (purple_xfer_get_filename(xfer
) != NULL
)
882 char *filename
= g_markup_escape_text(purple_xfer_get_filename(xfer
), -1);
883 if (purple_xfer_get_local_filename(xfer
)
884 && purple_xfer_get_type(xfer
) == PURPLE_XFER_RECEIVE
)
886 char *local
= g_markup_escape_text(purple_xfer_get_local_filename(xfer
), -1);
887 msg
= g_strdup_printf(_("Transfer of file <A HREF=\"file://%s\">%s</A> complete"),
892 msg
= g_strdup_printf(_("Transfer of file %s complete"),
897 msg
= g_strdup(_("File transfer complete"));
899 conv
= purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM
, xfer
->who
,
900 purple_xfer_get_account(xfer
));
903 purple_conversation_write(conv
, NULL
, msg
, PURPLE_MESSAGE_SYSTEM
, time(NULL
));
907 ui_ops
= purple_xfer_get_ui_ops(xfer
);
909 if (ui_ops
!= NULL
&& ui_ops
->update_progress
!= NULL
)
910 ui_ops
->update_progress(xfer
, purple_xfer_get_progress(xfer
));
914 purple_xfer_set_message(PurpleXfer
*xfer
, const char *message
)
916 g_return_if_fail(xfer
!= NULL
);
918 g_free(xfer
->message
);
919 xfer
->message
= g_strdup(message
);
923 purple_xfer_set_filename(PurpleXfer
*xfer
, const char *filename
)
925 g_return_if_fail(xfer
!= NULL
);
927 g_free(xfer
->filename
);
928 xfer
->filename
= g_strdup(filename
);
932 purple_xfer_set_local_filename(PurpleXfer
*xfer
, const char *filename
)
934 g_return_if_fail(xfer
!= NULL
);
936 g_free(xfer
->local_filename
);
937 xfer
->local_filename
= g_strdup(filename
);
941 purple_xfer_set_size(PurpleXfer
*xfer
, size_t size
)
943 g_return_if_fail(xfer
!= NULL
);
946 xfer
->bytes_remaining
= xfer
->size
- purple_xfer_get_bytes_sent(xfer
);
950 purple_xfer_set_bytes_sent(PurpleXfer
*xfer
, size_t bytes_sent
)
952 g_return_if_fail(xfer
!= NULL
);
954 xfer
->bytes_sent
= bytes_sent
;
955 xfer
->bytes_remaining
= purple_xfer_get_size(xfer
) - bytes_sent
;
959 purple_xfer_get_ui_ops(const PurpleXfer
*xfer
)
961 g_return_val_if_fail(xfer
!= NULL
, NULL
);
967 purple_xfer_set_init_fnc(PurpleXfer
*xfer
, void (*fnc
)(PurpleXfer
*))
969 g_return_if_fail(xfer
!= NULL
);
971 xfer
->ops
.init
= fnc
;
974 void purple_xfer_set_request_denied_fnc(PurpleXfer
*xfer
, void (*fnc
)(PurpleXfer
*))
976 g_return_if_fail(xfer
!= NULL
);
978 xfer
->ops
.request_denied
= fnc
;
982 purple_xfer_set_read_fnc(PurpleXfer
*xfer
, gssize (*fnc
)(guchar
**, PurpleXfer
*))
984 g_return_if_fail(xfer
!= NULL
);
986 xfer
->ops
.read
= fnc
;
990 purple_xfer_set_write_fnc(PurpleXfer
*xfer
,
991 gssize (*fnc
)(const guchar
*, size_t, PurpleXfer
*))
993 g_return_if_fail(xfer
!= NULL
);
995 xfer
->ops
.write
= fnc
;
999 purple_xfer_set_ack_fnc(PurpleXfer
*xfer
,
1000 void (*fnc
)(PurpleXfer
*, const guchar
*, size_t))
1002 g_return_if_fail(xfer
!= NULL
);
1004 xfer
->ops
.ack
= fnc
;
1008 purple_xfer_set_start_fnc(PurpleXfer
*xfer
, void (*fnc
)(PurpleXfer
*))
1010 g_return_if_fail(xfer
!= NULL
);
1012 xfer
->ops
.start
= fnc
;
1016 purple_xfer_set_end_fnc(PurpleXfer
*xfer
, void (*fnc
)(PurpleXfer
*))
1018 g_return_if_fail(xfer
!= NULL
);
1020 xfer
->ops
.end
= fnc
;
1024 purple_xfer_set_cancel_send_fnc(PurpleXfer
*xfer
, void (*fnc
)(PurpleXfer
*))
1026 g_return_if_fail(xfer
!= NULL
);
1028 xfer
->ops
.cancel_send
= fnc
;
1032 purple_xfer_set_cancel_recv_fnc(PurpleXfer
*xfer
, void (*fnc
)(PurpleXfer
*))
1034 g_return_if_fail(xfer
!= NULL
);
1036 xfer
->ops
.cancel_recv
= fnc
;
1040 purple_xfer_increase_buffer_size(PurpleXfer
*xfer
)
1042 xfer
->current_buffer_size
= MIN(xfer
->current_buffer_size
* 1.5,
1043 FT_MAX_BUFFER_SIZE
);
1047 purple_xfer_read(PurpleXfer
*xfer
, guchar
**buffer
)
1051 g_return_val_if_fail(xfer
!= NULL
, 0);
1052 g_return_val_if_fail(buffer
!= NULL
, 0);
1054 if (purple_xfer_get_size(xfer
) == 0)
1055 s
= xfer
->current_buffer_size
;
1057 s
= MIN(purple_xfer_get_bytes_remaining(xfer
), xfer
->current_buffer_size
);
1059 if (xfer
->ops
.read
!= NULL
) {
1060 r
= (xfer
->ops
.read
)(buffer
, xfer
);
1063 *buffer
= g_malloc0(s
);
1065 r
= read(xfer
->fd
, *buffer
, s
);
1066 if (r
< 0 && errno
== EAGAIN
)
1074 if (r
== xfer
->current_buffer_size
)
1076 * We managed to read the entire buffer. This means our this
1077 * network is fast and our buffer is too small, so make it
1080 purple_xfer_increase_buffer_size(xfer
);
1086 purple_xfer_write(PurpleXfer
*xfer
, const guchar
*buffer
, gsize size
)
1090 g_return_val_if_fail(xfer
!= NULL
, 0);
1091 g_return_val_if_fail(buffer
!= NULL
, 0);
1092 g_return_val_if_fail(size
!= 0, 0);
1094 s
= MIN(purple_xfer_get_bytes_remaining(xfer
), size
);
1096 if (xfer
->ops
.write
!= NULL
) {
1097 r
= (xfer
->ops
.write
)(buffer
, s
, xfer
);
1099 r
= write(xfer
->fd
, buffer
, s
);
1100 if (r
< 0 && errno
== EAGAIN
)
1103 if (r
>= 0 && (purple_xfer_get_bytes_sent(xfer
)+r
) >= purple_xfer_get_size(xfer
) &&
1104 !purple_xfer_is_completed(xfer
))
1105 purple_xfer_set_completed(xfer
, TRUE
);
1112 do_transfer(PurpleXfer
*xfer
)
1114 PurpleXferUiOps
*ui_ops
;
1115 guchar
*buffer
= NULL
;
1118 ui_ops
= purple_xfer_get_ui_ops(xfer
);
1120 if (xfer
->type
== PURPLE_XFER_RECEIVE
) {
1121 r
= purple_xfer_read(xfer
, &buffer
);
1124 if (ui_ops
&& ui_ops
->ui_write
)
1125 wc
= ui_ops
->ui_write(xfer
, buffer
, r
);
1127 wc
= fwrite(buffer
, 1, r
, xfer
->dest_fp
);
1130 purple_debug_error("filetransfer", "Unable to write whole buffer.\n");
1131 purple_xfer_cancel_local(xfer
);
1136 if ((purple_xfer_get_size(xfer
) > 0) &&
1137 ((purple_xfer_get_bytes_sent(xfer
)+r
) >= purple_xfer_get_size(xfer
)))
1138 purple_xfer_set_completed(xfer
, TRUE
);
1140 purple_xfer_cancel_remote(xfer
);
1144 } else if (xfer
->type
== PURPLE_XFER_SEND
) {
1146 size_t s
= MIN(purple_xfer_get_bytes_remaining(xfer
), xfer
->current_buffer_size
);
1147 PurpleXferPrivData
*priv
= g_hash_table_lookup(xfers_data
, xfer
);
1148 gboolean read
= TRUE
;
1150 /* this is so the prpl can keep the connection open
1151 if it needs to for some odd reason. */
1153 if (xfer
->watcher
) {
1154 purple_input_remove(xfer
->watcher
);
1161 if (priv
->buffer
->len
< s
) {
1162 s
-= priv
->buffer
->len
;
1170 if (ui_ops
&& ui_ops
->ui_read
) {
1171 gssize tmp
= ui_ops
->ui_read(xfer
, &buffer
, s
);
1174 * The UI claimed it was ready, but didn't have any data for
1175 * us... It will call purple_xfer_ui_ready when ready, which
1176 * sets back up this watcher.
1178 if (xfer
->watcher
!= 0) {
1179 purple_input_remove(xfer
->watcher
);
1183 /* Need to indicate the prpl is still ready... */
1184 priv
->ready
|= PURPLE_XFER_READY_PRPL
;
1186 g_return_if_reached();
1187 } else if (tmp
< 0) {
1188 purple_debug_error("filetransfer", "Unable to read whole buffer.\n");
1189 purple_xfer_cancel_local(xfer
);
1195 buffer
= g_malloc(s
);
1196 result
= fread(buffer
, 1, s
, xfer
->dest_fp
);
1198 purple_debug_error("filetransfer", "Unable to read whole buffer.\n");
1199 purple_xfer_cancel_local(xfer
);
1207 g_byte_array_append(priv
->buffer
, buffer
, result
);
1209 buffer
= priv
->buffer
->data
;
1210 result
= priv
->buffer
->len
;
1213 r
= purple_xfer_write(xfer
, buffer
, result
);
1216 purple_xfer_cancel_remote(xfer
);
1218 /* We don't free buffer if priv->buffer is set, because in
1219 that case buffer doesn't belong to us. */
1222 } else if (r
== result
) {
1224 * We managed to write the entire buffer. This means our
1225 * network is fast and our buffer is too small, so make it
1228 purple_xfer_increase_buffer_size(xfer
);
1230 if (ui_ops
&& ui_ops
->data_not_sent
)
1231 ui_ops
->data_not_sent(xfer
, buffer
+ r
, result
- r
);
1236 * Remove what we wrote
1237 * If we wrote the whole buffer the byte array will be empty
1238 * Otherwise we'll keep what wasn't sent for next time.
1241 g_byte_array_remove_range(priv
->buffer
, 0, r
);
1246 if (purple_xfer_get_size(xfer
) > 0)
1247 xfer
->bytes_remaining
-= r
;
1249 xfer
->bytes_sent
+= r
;
1251 if (xfer
->ops
.ack
!= NULL
)
1252 xfer
->ops
.ack(xfer
, buffer
, r
);
1256 if (ui_ops
!= NULL
&& ui_ops
->update_progress
!= NULL
)
1257 ui_ops
->update_progress(xfer
,
1258 purple_xfer_get_progress(xfer
));
1261 if (purple_xfer_is_completed(xfer
))
1262 purple_xfer_end(xfer
);
1266 transfer_cb(gpointer data
, gint source
, PurpleInputCondition condition
)
1268 PurpleXfer
*xfer
= data
;
1270 if (xfer
->dest_fp
== NULL
) {
1271 /* The UI is moderating its side manually */
1272 PurpleXferPrivData
*priv
= g_hash_table_lookup(xfers_data
, xfer
);
1273 if (0 == (priv
->ready
& PURPLE_XFER_READY_UI
)) {
1274 priv
->ready
|= PURPLE_XFER_READY_PRPL
;
1276 purple_input_remove(xfer
->watcher
);
1279 purple_debug_misc("xfer", "prpl is ready on ft %p, waiting for UI\n", xfer
);
1283 priv
->ready
= PURPLE_XFER_READY_NONE
;
1290 begin_transfer(PurpleXfer
*xfer
, PurpleInputCondition cond
)
1292 PurpleXferType type
= purple_xfer_get_type(xfer
);
1293 PurpleXferUiOps
*ui_ops
= purple_xfer_get_ui_ops(xfer
);
1295 if (xfer
->start_time
!= 0) {
1296 purple_debug_error("xfer", "Transfer is being started multiple times\n");
1297 g_return_if_reached();
1300 if (ui_ops
== NULL
|| (ui_ops
->ui_read
== NULL
&& ui_ops
->ui_write
== NULL
)) {
1301 xfer
->dest_fp
= g_fopen(purple_xfer_get_local_filename(xfer
),
1302 type
== PURPLE_XFER_RECEIVE
? "wb" : "rb");
1304 if (xfer
->dest_fp
== NULL
) {
1305 purple_xfer_show_file_error(xfer
, purple_xfer_get_local_filename(xfer
));
1306 purple_xfer_cancel_local(xfer
);
1310 fseek(xfer
->dest_fp
, xfer
->bytes_sent
, SEEK_SET
);
1314 xfer
->watcher
= purple_input_add(xfer
->fd
, cond
, transfer_cb
, xfer
);
1316 xfer
->start_time
= time(NULL
);
1318 if (xfer
->ops
.start
!= NULL
)
1319 xfer
->ops
.start(xfer
);
1323 connect_cb(gpointer data
, gint source
, const gchar
*error_message
)
1325 PurpleXfer
*xfer
= (PurpleXfer
*)data
;
1328 purple_xfer_cancel_local(xfer
);
1334 begin_transfer(xfer
, PURPLE_INPUT_READ
);
1338 purple_xfer_ui_ready(PurpleXfer
*xfer
)
1340 PurpleInputCondition cond
;
1341 PurpleXferType type
;
1342 PurpleXferPrivData
*priv
;
1344 g_return_if_fail(xfer
!= NULL
);
1346 priv
= g_hash_table_lookup(xfers_data
, xfer
);
1347 priv
->ready
|= PURPLE_XFER_READY_UI
;
1349 if (0 == (priv
->ready
& PURPLE_XFER_READY_PRPL
)) {
1350 purple_debug_misc("xfer", "UI is ready on ft %p, waiting for prpl\n", xfer
);
1354 purple_debug_misc("xfer", "UI (and prpl) ready on ft %p, so proceeding\n", xfer
);
1356 type
= purple_xfer_get_type(xfer
);
1357 if (type
== PURPLE_XFER_SEND
)
1358 cond
= PURPLE_INPUT_WRITE
;
1359 else /* if (type == PURPLE_XFER_RECEIVE) */
1360 cond
= PURPLE_INPUT_READ
;
1362 if (xfer
->watcher
== 0 && xfer
->fd
!= -1)
1363 xfer
->watcher
= purple_input_add(xfer
->fd
, cond
, transfer_cb
, xfer
);
1365 priv
->ready
= PURPLE_XFER_READY_NONE
;
1371 purple_xfer_prpl_ready(PurpleXfer
*xfer
)
1373 PurpleXferPrivData
*priv
;
1375 g_return_if_fail(xfer
!= NULL
);
1377 priv
= g_hash_table_lookup(xfers_data
, xfer
);
1378 priv
->ready
|= PURPLE_XFER_READY_PRPL
;
1380 /* I don't think fwrite/fread are ever *not* ready */
1381 if (xfer
->dest_fp
== NULL
&& 0 == (priv
->ready
& PURPLE_XFER_READY_UI
)) {
1382 purple_debug_misc("xfer", "prpl is ready on ft %p, waiting for UI\n", xfer
);
1386 purple_debug_misc("xfer", "Prpl (and UI) ready on ft %p, so proceeding\n", xfer
);
1388 priv
->ready
= PURPLE_XFER_READY_NONE
;
1394 purple_xfer_start(PurpleXfer
*xfer
, int fd
, const char *ip
,
1397 PurpleInputCondition cond
;
1398 PurpleXferType type
;
1400 g_return_if_fail(xfer
!= NULL
);
1401 g_return_if_fail(purple_xfer_get_type(xfer
) != PURPLE_XFER_UNKNOWN
);
1403 type
= purple_xfer_get_type(xfer
);
1405 purple_xfer_set_status(xfer
, PURPLE_XFER_STATUS_STARTED
);
1408 * FIXME 3.0.0 -- there's too much broken code depending on fd == 0
1409 * meaning "don't use a real fd"
1414 if (type
== PURPLE_XFER_RECEIVE
) {
1415 cond
= PURPLE_INPUT_READ
;
1418 xfer
->remote_ip
= g_strdup(ip
);
1419 xfer
->remote_port
= port
;
1421 /* Establish a file descriptor. */
1422 purple_proxy_connect(NULL
, xfer
->account
, xfer
->remote_ip
,
1423 xfer
->remote_port
, connect_cb
, xfer
);
1432 cond
= PURPLE_INPUT_WRITE
;
1437 begin_transfer(xfer
, cond
);
1441 purple_xfer_end(PurpleXfer
*xfer
)
1443 g_return_if_fail(xfer
!= NULL
);
1445 /* See if we are actually trying to cancel this. */
1446 if (!purple_xfer_is_completed(xfer
)) {
1447 purple_xfer_cancel_local(xfer
);
1451 xfer
->end_time
= time(NULL
);
1452 if (xfer
->ops
.end
!= NULL
)
1453 xfer
->ops
.end(xfer
);
1455 if (xfer
->watcher
!= 0) {
1456 purple_input_remove(xfer
->watcher
);
1463 if (xfer
->dest_fp
!= NULL
) {
1464 fclose(xfer
->dest_fp
);
1465 xfer
->dest_fp
= NULL
;
1468 purple_xfer_unref(xfer
);
1472 purple_xfer_add(PurpleXfer
*xfer
)
1474 PurpleXferUiOps
*ui_ops
;
1476 g_return_if_fail(xfer
!= NULL
);
1478 ui_ops
= purple_xfer_get_ui_ops(xfer
);
1480 if (ui_ops
!= NULL
&& ui_ops
->add_xfer
!= NULL
)
1481 ui_ops
->add_xfer(xfer
);
1485 purple_xfer_cancel_local(PurpleXfer
*xfer
)
1487 PurpleXferUiOps
*ui_ops
;
1490 g_return_if_fail(xfer
!= NULL
);
1492 /* TODO: We definitely want to close any open request dialogs associated
1493 with this transfer. However, in some cases the request dialog might
1494 own a reference on the xfer. This happens at least with the "%s wants
1495 to send you %s" dialog from purple_xfer_ask_recv(). In these cases
1496 the ref count will not be decremented when the request dialog is
1497 closed, so the ref count will never reach 0 and the xfer will never
1498 be freed. This is a memleak and should be fixed. It's not clear what
1499 the correct fix is. Probably requests should have a destroy function
1500 that is called when the request is destroyed. But also, ref counting
1501 xfer objects makes this code REALLY complicated. An alternate fix is
1502 to not ref count and instead just make sure the object still exists
1503 when we try to use it. */
1504 purple_request_close_with_handle(xfer
);
1506 purple_xfer_set_status(xfer
, PURPLE_XFER_STATUS_CANCEL_LOCAL
);
1507 xfer
->end_time
= time(NULL
);
1509 if (purple_xfer_get_filename(xfer
) != NULL
)
1511 msg
= g_strdup_printf(_("You cancelled the transfer of %s"),
1512 purple_xfer_get_filename(xfer
));
1516 msg
= g_strdup(_("File transfer cancelled"));
1518 purple_xfer_conversation_write(xfer
, msg
, FALSE
);
1521 if (purple_xfer_get_type(xfer
) == PURPLE_XFER_SEND
)
1523 if (xfer
->ops
.cancel_send
!= NULL
)
1524 xfer
->ops
.cancel_send(xfer
);
1528 if (xfer
->ops
.cancel_recv
!= NULL
)
1529 xfer
->ops
.cancel_recv(xfer
);
1532 if (xfer
->watcher
!= 0) {
1533 purple_input_remove(xfer
->watcher
);
1540 if (xfer
->dest_fp
!= NULL
) {
1541 fclose(xfer
->dest_fp
);
1542 xfer
->dest_fp
= NULL
;
1545 ui_ops
= purple_xfer_get_ui_ops(xfer
);
1547 if (ui_ops
!= NULL
&& ui_ops
->cancel_local
!= NULL
)
1548 ui_ops
->cancel_local(xfer
);
1550 xfer
->bytes_remaining
= 0;
1552 purple_xfer_unref(xfer
);
1556 purple_xfer_cancel_remote(PurpleXfer
*xfer
)
1558 PurpleXferUiOps
*ui_ops
;
1560 PurpleAccount
*account
;
1563 g_return_if_fail(xfer
!= NULL
);
1565 purple_request_close_with_handle(xfer
);
1566 purple_xfer_set_status(xfer
, PURPLE_XFER_STATUS_CANCEL_REMOTE
);
1567 xfer
->end_time
= time(NULL
);
1569 account
= purple_xfer_get_account(xfer
);
1570 buddy
= purple_find_buddy(account
, xfer
->who
);
1572 if (purple_xfer_get_filename(xfer
) != NULL
)
1574 msg
= g_strdup_printf(_("%s cancelled the transfer of %s"),
1575 buddy
? purple_buddy_get_alias(buddy
) : xfer
->who
, purple_xfer_get_filename(xfer
));
1579 msg
= g_strdup_printf(_("%s cancelled the file transfer"),
1580 buddy
? purple_buddy_get_alias(buddy
) : xfer
->who
);
1582 purple_xfer_conversation_write(xfer
, msg
, TRUE
);
1583 purple_xfer_error(purple_xfer_get_type(xfer
), account
, xfer
->who
, msg
);
1586 if (purple_xfer_get_type(xfer
) == PURPLE_XFER_SEND
)
1588 if (xfer
->ops
.cancel_send
!= NULL
)
1589 xfer
->ops
.cancel_send(xfer
);
1593 if (xfer
->ops
.cancel_recv
!= NULL
)
1594 xfer
->ops
.cancel_recv(xfer
);
1597 if (xfer
->watcher
!= 0) {
1598 purple_input_remove(xfer
->watcher
);
1605 if (xfer
->dest_fp
!= NULL
) {
1606 fclose(xfer
->dest_fp
);
1607 xfer
->dest_fp
= NULL
;
1610 ui_ops
= purple_xfer_get_ui_ops(xfer
);
1612 if (ui_ops
!= NULL
&& ui_ops
->cancel_remote
!= NULL
)
1613 ui_ops
->cancel_remote(xfer
);
1615 xfer
->bytes_remaining
= 0;
1617 purple_xfer_unref(xfer
);
1621 purple_xfer_error(PurpleXferType type
, PurpleAccount
*account
, const char *who
, const char *msg
)
1625 g_return_if_fail(msg
!= NULL
);
1626 g_return_if_fail(type
!= PURPLE_XFER_UNKNOWN
);
1630 buddy
= purple_find_buddy(account
, who
);
1632 who
= purple_buddy_get_alias(buddy
);
1635 if (type
== PURPLE_XFER_SEND
)
1636 title
= g_strdup_printf(_("File transfer to %s failed."), who
);
1638 title
= g_strdup_printf(_("File transfer from %s failed."), who
);
1640 purple_notify_error(NULL
, NULL
, title
, msg
);
1646 purple_xfer_update_progress(PurpleXfer
*xfer
)
1648 PurpleXferUiOps
*ui_ops
;
1650 g_return_if_fail(xfer
!= NULL
);
1652 ui_ops
= purple_xfer_get_ui_ops(xfer
);
1653 if (ui_ops
!= NULL
&& ui_ops
->update_progress
!= NULL
)
1654 ui_ops
->update_progress(xfer
, purple_xfer_get_progress(xfer
));
1658 purple_xfer_get_thumbnail(const PurpleXfer
*xfer
, gsize
*len
)
1660 PurpleXferPrivData
*priv
= g_hash_table_lookup(xfers_data
, xfer
);
1663 *len
= priv
->thumbnail_size
;
1665 return priv
->thumbnail_data
;
1669 purple_xfer_get_thumbnail_mimetype(const PurpleXfer
*xfer
)
1671 PurpleXferPrivData
*priv
= g_hash_table_lookup(xfers_data
, xfer
);
1673 return priv
->thumbnail_mimetype
;
1677 purple_xfer_set_thumbnail(PurpleXfer
*xfer
, gconstpointer thumbnail
,
1678 gsize size
, const gchar
*mimetype
)
1680 PurpleXferPrivData
*priv
= g_hash_table_lookup(xfers_data
, xfer
);
1682 g_free(priv
->thumbnail_data
);
1683 g_free(priv
->thumbnail_mimetype
);
1685 if (thumbnail
&& size
> 0) {
1686 priv
->thumbnail_data
= g_memdup(thumbnail
, size
);
1687 priv
->thumbnail_size
= size
;
1688 priv
->thumbnail_mimetype
= g_strdup(mimetype
);
1690 priv
->thumbnail_data
= NULL
;
1691 priv
->thumbnail_size
= 0;
1692 priv
->thumbnail_mimetype
= NULL
;
1697 purple_xfer_prepare_thumbnail(PurpleXfer
*xfer
, const gchar
*formats
)
1699 if (xfer
->ui_ops
->add_thumbnail
) {
1700 xfer
->ui_ops
->add_thumbnail(xfer
, formats
);
1704 /**************************************************************************
1705 * File Transfer Subsystem API
1706 **************************************************************************/
1708 purple_xfers_get_handle(void) {
1709 static int handle
= 0;
1715 purple_xfers_init(void) {
1716 void *handle
= purple_xfers_get_handle();
1718 xfers_data
= g_hash_table_new_full(g_direct_hash
, g_direct_equal
,
1719 NULL
, purple_xfer_priv_data_destroy
);
1721 /* register signals */
1722 purple_signal_register(handle
, "file-recv-accept",
1723 purple_marshal_VOID__POINTER
, NULL
, 1,
1724 purple_value_new(PURPLE_TYPE_SUBTYPE
,
1725 PURPLE_SUBTYPE_XFER
));
1726 purple_signal_register(handle
, "file-send-accept",
1727 purple_marshal_VOID__POINTER
, NULL
, 1,
1728 purple_value_new(PURPLE_TYPE_SUBTYPE
,
1729 PURPLE_SUBTYPE_XFER
));
1730 purple_signal_register(handle
, "file-recv-start",
1731 purple_marshal_VOID__POINTER
, NULL
, 1,
1732 purple_value_new(PURPLE_TYPE_SUBTYPE
,
1733 PURPLE_SUBTYPE_XFER
));
1734 purple_signal_register(handle
, "file-send-start",
1735 purple_marshal_VOID__POINTER
, NULL
, 1,
1736 purple_value_new(PURPLE_TYPE_SUBTYPE
,
1737 PURPLE_SUBTYPE_XFER
));
1738 purple_signal_register(handle
, "file-send-cancel",
1739 purple_marshal_VOID__POINTER
, NULL
, 1,
1740 purple_value_new(PURPLE_TYPE_SUBTYPE
,
1741 PURPLE_SUBTYPE_XFER
));
1742 purple_signal_register(handle
, "file-recv-cancel",
1743 purple_marshal_VOID__POINTER
, NULL
, 1,
1744 purple_value_new(PURPLE_TYPE_SUBTYPE
,
1745 PURPLE_SUBTYPE_XFER
));
1746 purple_signal_register(handle
, "file-send-complete",
1747 purple_marshal_VOID__POINTER
, NULL
, 1,
1748 purple_value_new(PURPLE_TYPE_SUBTYPE
,
1749 PURPLE_SUBTYPE_XFER
));
1750 purple_signal_register(handle
, "file-recv-complete",
1751 purple_marshal_VOID__POINTER
, NULL
, 1,
1752 purple_value_new(PURPLE_TYPE_SUBTYPE
,
1753 PURPLE_SUBTYPE_XFER
));
1754 purple_signal_register(handle
, "file-recv-request",
1755 purple_marshal_VOID__POINTER
, NULL
, 1,
1756 purple_value_new(PURPLE_TYPE_SUBTYPE
,
1757 PURPLE_SUBTYPE_XFER
));
1761 purple_xfers_uninit(void)
1763 void *handle
= purple_xfers_get_handle();
1765 purple_signals_disconnect_by_handle(handle
);
1766 purple_signals_unregister_by_instance(handle
);
1768 g_hash_table_destroy(xfers_data
);
1773 purple_xfers_set_ui_ops(PurpleXferUiOps
*ops
) {
1778 purple_xfers_get_ui_ops(void) {