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,
63 static int purple_xfer_choose_file(PurpleXfer
*xfer
);
66 purple_xfer_priv_data_destroy(gpointer data
)
68 PurpleXferPrivData
*priv
= data
;
71 g_byte_array_free(priv
->buffer
, TRUE
);
77 purple_xfers_get_all()
83 purple_xfer_new(PurpleAccount
*account
, PurpleXferType type
, const char *who
)
86 PurpleXferUiOps
*ui_ops
;
87 PurpleXferPrivData
*priv
;
89 g_return_val_if_fail(type
!= PURPLE_XFER_UNKNOWN
, NULL
);
90 g_return_val_if_fail(account
!= NULL
, NULL
);
91 g_return_val_if_fail(who
!= NULL
, NULL
);
93 xfer
= g_new0(PurpleXfer
, 1);
94 PURPLE_DBUS_REGISTER_POINTER(xfer
, PurpleXfer
);
98 xfer
->account
= account
;
99 xfer
->who
= g_strdup(who
);
100 xfer
->ui_ops
= ui_ops
= purple_xfers_get_ui_ops();
101 xfer
->message
= NULL
;
102 xfer
->current_buffer_size
= FT_INITIAL_BUFFER_SIZE
;
105 priv
= g_new0(PurpleXferPrivData
, 1);
106 priv
->ready
= PURPLE_XFER_READY_NONE
;
108 if (ui_ops
&& ui_ops
->data_not_sent
) {
109 /* If the ui will handle unsent data no need for buffer */
112 priv
->buffer
= g_byte_array_sized_new(FT_INITIAL_BUFFER_SIZE
);
115 g_hash_table_insert(xfers_data
, xfer
, priv
);
117 ui_ops
= purple_xfer_get_ui_ops(xfer
);
119 if (ui_ops
!= NULL
&& ui_ops
->new_xfer
!= NULL
)
120 ui_ops
->new_xfer(xfer
);
122 xfers
= g_list_prepend(xfers
, xfer
);
127 purple_xfer_destroy(PurpleXfer
*xfer
)
129 PurpleXferUiOps
*ui_ops
;
130 PurpleXferPrivData
*priv
;
132 g_return_if_fail(xfer
!= NULL
);
134 priv
= g_hash_table_lookup(xfers_data
, xfer
);
136 /* Close the file browser, if it's open */
137 purple_request_close_with_handle(xfer
);
139 if (purple_xfer_get_status(xfer
) == PURPLE_XFER_STATUS_STARTED
)
140 purple_xfer_cancel_local(xfer
);
142 ui_ops
= purple_xfer_get_ui_ops(xfer
);
144 if (ui_ops
!= NULL
&& ui_ops
->destroy
!= NULL
)
145 ui_ops
->destroy(xfer
);
148 g_free(xfer
->filename
);
149 g_free(xfer
->remote_ip
);
150 g_free(xfer
->local_filename
);
152 g_hash_table_remove(xfers_data
, xfer
);
154 PURPLE_DBUS_UNREGISTER_POINTER(xfer
);
155 xfers
= g_list_remove(xfers
, xfer
);
160 purple_xfer_ref(PurpleXfer
*xfer
)
162 g_return_if_fail(xfer
!= NULL
);
168 purple_xfer_unref(PurpleXfer
*xfer
)
170 g_return_if_fail(xfer
!= NULL
);
171 g_return_if_fail(xfer
->ref
> 0);
176 purple_xfer_destroy(xfer
);
180 purple_xfer_set_status(PurpleXfer
*xfer
, PurpleXferStatusType status
)
182 g_return_if_fail(xfer
!= NULL
);
184 if (xfer
->status
== status
)
187 xfer
->status
= status
;
189 if(xfer
->type
== PURPLE_XFER_SEND
) {
191 case PURPLE_XFER_STATUS_ACCEPTED
:
192 purple_signal_emit(purple_xfers_get_handle(), "file-send-accept", xfer
);
194 case PURPLE_XFER_STATUS_STARTED
:
195 purple_signal_emit(purple_xfers_get_handle(), "file-send-start", xfer
);
197 case PURPLE_XFER_STATUS_DONE
:
198 purple_signal_emit(purple_xfers_get_handle(), "file-send-complete", xfer
);
200 case PURPLE_XFER_STATUS_CANCEL_LOCAL
:
201 case PURPLE_XFER_STATUS_CANCEL_REMOTE
:
202 purple_signal_emit(purple_xfers_get_handle(), "file-send-cancel", xfer
);
207 } else if(xfer
->type
== PURPLE_XFER_RECEIVE
) {
209 case PURPLE_XFER_STATUS_ACCEPTED
:
210 purple_signal_emit(purple_xfers_get_handle(), "file-recv-accept", xfer
);
212 case PURPLE_XFER_STATUS_STARTED
:
213 purple_signal_emit(purple_xfers_get_handle(), "file-recv-start", xfer
);
215 case PURPLE_XFER_STATUS_DONE
:
216 purple_signal_emit(purple_xfers_get_handle(), "file-recv-complete", xfer
);
218 case PURPLE_XFER_STATUS_CANCEL_LOCAL
:
219 case PURPLE_XFER_STATUS_CANCEL_REMOTE
:
220 purple_signal_emit(purple_xfers_get_handle(), "file-recv-cancel", xfer
);
228 void purple_xfer_conversation_write(PurpleXfer
*xfer
, char *message
, gboolean is_error
)
230 PurpleConversation
*conv
= NULL
;
231 PurpleMessageFlags flags
= PURPLE_MESSAGE_SYSTEM
;
234 g_return_if_fail(xfer
!= NULL
);
235 g_return_if_fail(message
!= NULL
);
237 conv
= purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM
, xfer
->who
,
238 purple_xfer_get_account(xfer
));
243 escaped
= g_markup_escape_text(message
, -1);
246 flags
= PURPLE_MESSAGE_ERROR
;
248 purple_conversation_write(conv
, NULL
, escaped
, flags
, time(NULL
));
252 static void purple_xfer_show_file_error(PurpleXfer
*xfer
, const char *filename
)
255 gchar
*msg
= NULL
, *utf8
;
256 PurpleXferType xfer_type
= purple_xfer_get_type(xfer
);
257 PurpleAccount
*account
= purple_xfer_get_account(xfer
);
259 utf8
= g_filename_to_utf8(filename
, -1, NULL
, NULL
, NULL
);
261 case PURPLE_XFER_SEND
:
262 msg
= g_strdup_printf(_("Error reading %s: \n%s.\n"),
263 utf8
, g_strerror(err
));
265 case PURPLE_XFER_RECEIVE
:
266 msg
= g_strdup_printf(_("Error writing %s: \n%s.\n"),
267 utf8
, g_strerror(err
));
270 msg
= g_strdup_printf(_("Error accessing %s: \n%s.\n"),
271 utf8
, g_strerror(err
));
276 purple_xfer_conversation_write(xfer
, msg
, TRUE
);
277 purple_xfer_error(xfer_type
, account
, xfer
->who
, msg
);
282 purple_xfer_choose_file_ok_cb(void *user_data
, const char *filename
)
288 xfer
= (PurpleXfer
*)user_data
;
290 if (g_stat(filename
, &st
) != 0) {
291 /* File not found. */
292 if (purple_xfer_get_type(xfer
) == PURPLE_XFER_RECEIVE
) {
298 dir
= g_path_get_dirname(filename
);
300 if (g_access(dir
, mode
) == 0) {
301 purple_xfer_request_accepted(xfer
, filename
);
303 purple_xfer_ref(xfer
);
304 purple_notify_message(
305 NULL
, PURPLE_NOTIFY_MSG_ERROR
, NULL
,
306 _("Directory is not writable."), NULL
,
307 (PurpleNotifyCloseCallback
)purple_xfer_choose_file
, xfer
);
313 purple_xfer_show_file_error(xfer
, filename
);
314 purple_xfer_request_denied(xfer
);
317 else if ((purple_xfer_get_type(xfer
) == PURPLE_XFER_SEND
) &&
320 purple_notify_error(NULL
, NULL
,
321 _("Cannot send a file of 0 bytes."), NULL
);
323 purple_xfer_request_denied(xfer
);
325 else if ((purple_xfer_get_type(xfer
) == PURPLE_XFER_SEND
) &&
326 S_ISDIR(st
.st_mode
)) {
328 * XXX - Sending a directory should be valid for some protocols.
330 purple_notify_error(NULL
, NULL
,
331 _("Cannot send a directory."), NULL
);
333 purple_xfer_request_denied(xfer
);
335 else if ((purple_xfer_get_type(xfer
) == PURPLE_XFER_RECEIVE
) &&
336 S_ISDIR(st
.st_mode
)) {
338 utf8
= g_filename_to_utf8(filename
, -1, NULL
, NULL
, NULL
);
339 msg
= g_strdup_printf(
340 _("%s is not a regular file. Cowardly refusing to overwrite it.\n"), utf8
);
342 purple_notify_error(NULL
, NULL
, msg
, NULL
);
344 purple_xfer_request_denied(xfer
);
347 purple_xfer_request_accepted(xfer
, filename
);
350 purple_xfer_unref(xfer
);
354 purple_xfer_choose_file_cancel_cb(void *user_data
, const char *filename
)
356 PurpleXfer
*xfer
= (PurpleXfer
*)user_data
;
358 purple_xfer_set_status(xfer
, PURPLE_XFER_STATUS_CANCEL_LOCAL
);
359 purple_xfer_request_denied(xfer
);
363 purple_xfer_choose_file(PurpleXfer
*xfer
)
365 purple_request_file(xfer
, NULL
, purple_xfer_get_filename(xfer
),
366 (purple_xfer_get_type(xfer
) == PURPLE_XFER_RECEIVE
),
367 G_CALLBACK(purple_xfer_choose_file_ok_cb
),
368 G_CALLBACK(purple_xfer_choose_file_cancel_cb
),
369 purple_xfer_get_account(xfer
), xfer
->who
, NULL
,
376 cancel_recv_cb(PurpleXfer
*xfer
)
378 purple_xfer_set_status(xfer
, PURPLE_XFER_STATUS_CANCEL_LOCAL
);
379 purple_xfer_request_denied(xfer
);
380 purple_xfer_unref(xfer
);
386 purple_xfer_ask_recv(PurpleXfer
*xfer
)
388 char *buf
, *size_buf
;
391 /* If we have already accepted the request, ask the destination file
393 if (purple_xfer_get_status(xfer
) != PURPLE_XFER_STATUS_ACCEPTED
) {
394 PurpleBuddy
*buddy
= purple_find_buddy(xfer
->account
, xfer
->who
);
396 if (purple_xfer_get_filename(xfer
) != NULL
)
398 size
= purple_xfer_get_size(xfer
);
399 size_buf
= purple_str_size_to_units(size
);
400 buf
= g_strdup_printf(_("%s wants to send you %s (%s)"),
401 buddy
? purple_buddy_get_alias(buddy
) : xfer
->who
,
402 purple_xfer_get_filename(xfer
), size_buf
);
407 buf
= g_strdup_printf(_("%s wants to send you a file"),
408 buddy
? purple_buddy_get_alias(buddy
) : xfer
->who
);
411 if (xfer
->message
!= NULL
)
412 serv_got_im(purple_account_get_connection(xfer
->account
),
413 xfer
->who
, xfer
->message
, 0, time(NULL
));
415 purple_request_accept_cancel(xfer
, NULL
, buf
, NULL
,
416 PURPLE_DEFAULT_ACTION_NONE
,
417 xfer
->account
, xfer
->who
, NULL
,
419 G_CALLBACK(purple_xfer_choose_file
),
420 G_CALLBACK(cancel_recv_cb
));
424 purple_xfer_choose_file(xfer
);
428 ask_accept_ok(PurpleXfer
*xfer
)
430 purple_xfer_request_accepted(xfer
, NULL
);
436 ask_accept_cancel(PurpleXfer
*xfer
)
438 purple_xfer_request_denied(xfer
);
439 purple_xfer_unref(xfer
);
445 purple_xfer_ask_accept(PurpleXfer
*xfer
)
447 char *buf
, *buf2
= NULL
;
448 PurpleBuddy
*buddy
= purple_find_buddy(xfer
->account
, xfer
->who
);
450 buf
= g_strdup_printf(_("Accept file transfer request from %s?"),
451 buddy
? purple_buddy_get_alias(buddy
) : xfer
->who
);
452 if (purple_xfer_get_remote_ip(xfer
) &&
453 purple_xfer_get_remote_port(xfer
))
454 buf2
= g_strdup_printf(_("A file is available for download from:\n"
455 "Remote host: %s\nRemote port: %d"),
456 purple_xfer_get_remote_ip(xfer
),
457 purple_xfer_get_remote_port(xfer
));
458 purple_request_accept_cancel(xfer
, NULL
, buf
, buf2
,
459 PURPLE_DEFAULT_ACTION_NONE
,
460 xfer
->account
, xfer
->who
, NULL
,
462 G_CALLBACK(ask_accept_ok
),
463 G_CALLBACK(ask_accept_cancel
));
469 purple_xfer_request(PurpleXfer
*xfer
)
471 g_return_if_fail(xfer
!= NULL
);
472 g_return_if_fail(xfer
->ops
.init
!= NULL
);
474 purple_xfer_ref(xfer
);
476 if (purple_xfer_get_type(xfer
) == PURPLE_XFER_RECEIVE
)
478 purple_signal_emit(purple_xfers_get_handle(), "file-recv-request", xfer
);
479 if (purple_xfer_get_status(xfer
) == PURPLE_XFER_STATUS_CANCEL_LOCAL
)
481 /* The file-transfer was cancelled by a plugin */
482 purple_xfer_cancel_local(xfer
);
484 else if (purple_xfer_get_filename(xfer
) ||
485 purple_xfer_get_status(xfer
) == PURPLE_XFER_STATUS_ACCEPTED
)
487 gchar
* message
= NULL
;
488 PurpleBuddy
*buddy
= purple_find_buddy(xfer
->account
, xfer
->who
);
489 message
= g_strdup_printf(_("%s is offering to send file %s"),
490 buddy
? purple_buddy_get_alias(buddy
) : xfer
->who
, purple_xfer_get_filename(xfer
));
491 purple_xfer_conversation_write(xfer
, message
, FALSE
);
493 /* Ask for a filename to save to if it's not already given by a plugin */
494 if (xfer
->local_filename
== NULL
)
495 purple_xfer_ask_recv(xfer
);
499 purple_xfer_ask_accept(xfer
);
504 purple_xfer_choose_file(xfer
);
509 purple_xfer_request_accepted(PurpleXfer
*xfer
, const char *filename
)
513 char *msg
, *utf8
, *base
;
514 PurpleAccount
*account
;
520 type
= purple_xfer_get_type(xfer
);
521 account
= purple_xfer_get_account(xfer
);
523 if (!filename
&& type
== PURPLE_XFER_RECEIVE
) {
524 xfer
->status
= PURPLE_XFER_STATUS_ACCEPTED
;
525 xfer
->ops
.init(xfer
);
529 buddy
= purple_find_buddy(account
, xfer
->who
);
531 if (type
== PURPLE_XFER_SEND
) {
533 /* Check the filename. */
534 PurpleXferUiOps
*ui_ops
;
535 ui_ops
= purple_xfer_get_ui_ops(xfer
);
538 if (g_strrstr(filename
, "../") || g_strrstr(filename
, "..\\"))
540 if (g_strrstr(filename
, "../"))
543 utf8
= g_filename_to_utf8(filename
, -1, NULL
, NULL
, NULL
);
545 msg
= g_strdup_printf(_("%s is not a valid filename.\n"), utf8
);
546 purple_xfer_error(type
, account
, xfer
->who
, msg
);
550 purple_xfer_unref(xfer
);
554 if (ui_ops
== NULL
|| (ui_ops
->ui_read
== NULL
&& ui_ops
->ui_write
== NULL
)) {
555 if (g_stat(filename
, &st
) == -1) {
556 purple_xfer_show_file_error(xfer
, filename
);
557 purple_xfer_unref(xfer
);
561 purple_xfer_set_local_filename(xfer
, filename
);
562 purple_xfer_set_size(xfer
, st
.st_size
);
564 utf8
= g_strdup(filename
);
565 purple_xfer_set_local_filename(xfer
, filename
);
568 base
= g_path_get_basename(filename
);
569 utf8
= g_filename_to_utf8(base
, -1, NULL
, NULL
, NULL
);
571 purple_xfer_set_filename(xfer
, utf8
);
573 msg
= g_strdup_printf(_("Offering to send %s to %s"),
574 utf8
, buddy
? purple_buddy_get_alias(buddy
) : xfer
->who
);
576 purple_xfer_conversation_write(xfer
, msg
, FALSE
);
580 /* Receiving a file */
581 xfer
->status
= PURPLE_XFER_STATUS_ACCEPTED
;
582 purple_xfer_set_local_filename(xfer
, filename
);
584 msg
= g_strdup_printf(_("Starting transfer of %s from %s"),
585 xfer
->filename
, buddy
? purple_buddy_get_alias(buddy
) : xfer
->who
);
586 purple_xfer_conversation_write(xfer
, msg
, FALSE
);
590 purple_xfer_add(xfer
);
591 xfer
->ops
.init(xfer
);
596 purple_xfer_request_denied(PurpleXfer
*xfer
)
598 g_return_if_fail(xfer
!= NULL
);
600 if (xfer
->ops
.request_denied
!= NULL
)
601 xfer
->ops
.request_denied(xfer
);
603 purple_xfer_unref(xfer
);
607 purple_xfer_get_type(const PurpleXfer
*xfer
)
609 g_return_val_if_fail(xfer
!= NULL
, PURPLE_XFER_UNKNOWN
);
615 purple_xfer_get_account(const PurpleXfer
*xfer
)
617 g_return_val_if_fail(xfer
!= NULL
, NULL
);
619 return xfer
->account
;
623 purple_xfer_get_remote_user(const PurpleXfer
*xfer
)
625 g_return_val_if_fail(xfer
!= NULL
, NULL
);
630 purple_xfer_get_status(const PurpleXfer
*xfer
)
632 g_return_val_if_fail(xfer
!= NULL
, PURPLE_XFER_STATUS_UNKNOWN
);
638 purple_xfer_is_canceled(const PurpleXfer
*xfer
)
640 g_return_val_if_fail(xfer
!= NULL
, TRUE
);
642 if ((purple_xfer_get_status(xfer
) == PURPLE_XFER_STATUS_CANCEL_LOCAL
) ||
643 (purple_xfer_get_status(xfer
) == PURPLE_XFER_STATUS_CANCEL_REMOTE
))
650 purple_xfer_is_completed(const PurpleXfer
*xfer
)
652 g_return_val_if_fail(xfer
!= NULL
, TRUE
);
654 return (purple_xfer_get_status(xfer
) == PURPLE_XFER_STATUS_DONE
);
658 purple_xfer_get_filename(const PurpleXfer
*xfer
)
660 g_return_val_if_fail(xfer
!= NULL
, NULL
);
662 return xfer
->filename
;
666 purple_xfer_get_local_filename(const PurpleXfer
*xfer
)
668 g_return_val_if_fail(xfer
!= NULL
, NULL
);
670 return xfer
->local_filename
;
674 purple_xfer_get_bytes_sent(const PurpleXfer
*xfer
)
676 g_return_val_if_fail(xfer
!= NULL
, 0);
678 return xfer
->bytes_sent
;
682 purple_xfer_get_bytes_remaining(const PurpleXfer
*xfer
)
684 g_return_val_if_fail(xfer
!= NULL
, 0);
686 return xfer
->bytes_remaining
;
690 purple_xfer_get_size(const PurpleXfer
*xfer
)
692 g_return_val_if_fail(xfer
!= NULL
, 0);
698 purple_xfer_get_progress(const PurpleXfer
*xfer
)
700 g_return_val_if_fail(xfer
!= NULL
, 0.0);
702 if (purple_xfer_get_size(xfer
) == 0)
705 return ((double)purple_xfer_get_bytes_sent(xfer
) /
706 (double)purple_xfer_get_size(xfer
));
710 purple_xfer_get_local_port(const PurpleXfer
*xfer
)
712 g_return_val_if_fail(xfer
!= NULL
, -1);
714 return xfer
->local_port
;
718 purple_xfer_get_remote_ip(const PurpleXfer
*xfer
)
720 g_return_val_if_fail(xfer
!= NULL
, NULL
);
722 return xfer
->remote_ip
;
726 purple_xfer_get_remote_port(const PurpleXfer
*xfer
)
728 g_return_val_if_fail(xfer
!= NULL
, -1);
730 return xfer
->remote_port
;
734 purple_xfer_get_start_time(const PurpleXfer
*xfer
)
736 g_return_val_if_fail(xfer
!= NULL
, 0);
738 return xfer
->start_time
;
742 purple_xfer_get_end_time(const PurpleXfer
*xfer
)
744 g_return_val_if_fail(xfer
!= NULL
, 0);
746 return xfer
->end_time
;
750 purple_xfer_set_completed(PurpleXfer
*xfer
, gboolean completed
)
752 PurpleXferUiOps
*ui_ops
;
754 g_return_if_fail(xfer
!= NULL
);
756 if (completed
== TRUE
) {
758 PurpleConversation
*conv
;
760 purple_xfer_set_status(xfer
, PURPLE_XFER_STATUS_DONE
);
762 if (purple_xfer_get_filename(xfer
) != NULL
)
764 char *filename
= g_markup_escape_text(purple_xfer_get_filename(xfer
), -1);
765 if (purple_xfer_get_local_filename(xfer
)
766 && purple_xfer_get_type(xfer
) == PURPLE_XFER_RECEIVE
)
768 char *local
= g_markup_escape_text(purple_xfer_get_local_filename(xfer
), -1);
769 msg
= g_strdup_printf(_("Transfer of file <A HREF=\"file://%s\">%s</A> complete"),
774 msg
= g_strdup_printf(_("Transfer of file %s complete"),
779 msg
= g_strdup(_("File transfer complete"));
781 conv
= purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM
, xfer
->who
,
782 purple_xfer_get_account(xfer
));
785 purple_conversation_write(conv
, NULL
, msg
, PURPLE_MESSAGE_SYSTEM
, time(NULL
));
789 ui_ops
= purple_xfer_get_ui_ops(xfer
);
791 if (ui_ops
!= NULL
&& ui_ops
->update_progress
!= NULL
)
792 ui_ops
->update_progress(xfer
, purple_xfer_get_progress(xfer
));
796 purple_xfer_set_message(PurpleXfer
*xfer
, const char *message
)
798 g_return_if_fail(xfer
!= NULL
);
800 g_free(xfer
->message
);
801 xfer
->message
= g_strdup(message
);
805 purple_xfer_set_filename(PurpleXfer
*xfer
, const char *filename
)
807 g_return_if_fail(xfer
!= NULL
);
809 g_free(xfer
->filename
);
810 xfer
->filename
= g_strdup(filename
);
814 purple_xfer_set_local_filename(PurpleXfer
*xfer
, const char *filename
)
816 g_return_if_fail(xfer
!= NULL
);
818 g_free(xfer
->local_filename
);
819 xfer
->local_filename
= g_strdup(filename
);
823 purple_xfer_set_size(PurpleXfer
*xfer
, size_t size
)
825 g_return_if_fail(xfer
!= NULL
);
828 xfer
->bytes_remaining
= xfer
->size
- purple_xfer_get_bytes_sent(xfer
);
832 purple_xfer_set_bytes_sent(PurpleXfer
*xfer
, size_t bytes_sent
)
834 g_return_if_fail(xfer
!= NULL
);
836 xfer
->bytes_sent
= bytes_sent
;
837 xfer
->bytes_remaining
= purple_xfer_get_size(xfer
) - bytes_sent
;
841 purple_xfer_get_ui_ops(const PurpleXfer
*xfer
)
843 g_return_val_if_fail(xfer
!= NULL
, NULL
);
849 purple_xfer_set_init_fnc(PurpleXfer
*xfer
, void (*fnc
)(PurpleXfer
*))
851 g_return_if_fail(xfer
!= NULL
);
853 xfer
->ops
.init
= fnc
;
856 void purple_xfer_set_request_denied_fnc(PurpleXfer
*xfer
, void (*fnc
)(PurpleXfer
*))
858 g_return_if_fail(xfer
!= NULL
);
860 xfer
->ops
.request_denied
= fnc
;
864 purple_xfer_set_read_fnc(PurpleXfer
*xfer
, gssize (*fnc
)(guchar
**, PurpleXfer
*))
866 g_return_if_fail(xfer
!= NULL
);
868 xfer
->ops
.read
= fnc
;
872 purple_xfer_set_write_fnc(PurpleXfer
*xfer
,
873 gssize (*fnc
)(const guchar
*, size_t, PurpleXfer
*))
875 g_return_if_fail(xfer
!= NULL
);
877 xfer
->ops
.write
= fnc
;
881 purple_xfer_set_ack_fnc(PurpleXfer
*xfer
,
882 void (*fnc
)(PurpleXfer
*, const guchar
*, size_t))
884 g_return_if_fail(xfer
!= NULL
);
890 purple_xfer_set_start_fnc(PurpleXfer
*xfer
, void (*fnc
)(PurpleXfer
*))
892 g_return_if_fail(xfer
!= NULL
);
894 xfer
->ops
.start
= fnc
;
898 purple_xfer_set_end_fnc(PurpleXfer
*xfer
, void (*fnc
)(PurpleXfer
*))
900 g_return_if_fail(xfer
!= NULL
);
906 purple_xfer_set_cancel_send_fnc(PurpleXfer
*xfer
, void (*fnc
)(PurpleXfer
*))
908 g_return_if_fail(xfer
!= NULL
);
910 xfer
->ops
.cancel_send
= fnc
;
914 purple_xfer_set_cancel_recv_fnc(PurpleXfer
*xfer
, void (*fnc
)(PurpleXfer
*))
916 g_return_if_fail(xfer
!= NULL
);
918 xfer
->ops
.cancel_recv
= fnc
;
922 purple_xfer_increase_buffer_size(PurpleXfer
*xfer
)
924 xfer
->current_buffer_size
= MIN(xfer
->current_buffer_size
* 1.5,
929 purple_xfer_read(PurpleXfer
*xfer
, guchar
**buffer
)
933 g_return_val_if_fail(xfer
!= NULL
, 0);
934 g_return_val_if_fail(buffer
!= NULL
, 0);
936 if (purple_xfer_get_size(xfer
) == 0)
937 s
= xfer
->current_buffer_size
;
939 s
= MIN(purple_xfer_get_bytes_remaining(xfer
), xfer
->current_buffer_size
);
941 if (xfer
->ops
.read
!= NULL
) {
942 r
= (xfer
->ops
.read
)(buffer
, xfer
);
945 *buffer
= g_malloc0(s
);
947 r
= read(xfer
->fd
, *buffer
, s
);
948 if (r
< 0 && errno
== EAGAIN
)
956 if (r
== xfer
->current_buffer_size
)
958 * We managed to read the entire buffer. This means our this
959 * network is fast and our buffer is too small, so make it
962 purple_xfer_increase_buffer_size(xfer
);
968 purple_xfer_write(PurpleXfer
*xfer
, const guchar
*buffer
, gsize size
)
972 g_return_val_if_fail(xfer
!= NULL
, 0);
973 g_return_val_if_fail(buffer
!= NULL
, 0);
974 g_return_val_if_fail(size
!= 0, 0);
976 s
= MIN(purple_xfer_get_bytes_remaining(xfer
), size
);
978 if (xfer
->ops
.write
!= NULL
) {
979 r
= (xfer
->ops
.write
)(buffer
, s
, xfer
);
981 r
= write(xfer
->fd
, buffer
, s
);
982 if (r
< 0 && errno
== EAGAIN
)
990 do_transfer(PurpleXfer
*xfer
)
992 PurpleXferUiOps
*ui_ops
;
993 guchar
*buffer
= NULL
;
996 ui_ops
= purple_xfer_get_ui_ops(xfer
);
998 if (xfer
->type
== PURPLE_XFER_RECEIVE
) {
999 r
= purple_xfer_read(xfer
, &buffer
);
1002 if (ui_ops
&& ui_ops
->ui_write
)
1003 wc
= ui_ops
->ui_write(xfer
, buffer
, r
);
1005 wc
= fwrite(buffer
, 1, r
, xfer
->dest_fp
);
1008 purple_debug_error("filetransfer", "Unable to write whole buffer.\n");
1009 purple_xfer_cancel_local(xfer
);
1015 purple_xfer_cancel_remote(xfer
);
1019 } else if (xfer
->type
== PURPLE_XFER_SEND
) {
1021 size_t s
= MIN(purple_xfer_get_bytes_remaining(xfer
), xfer
->current_buffer_size
);
1022 PurpleXferPrivData
*priv
= g_hash_table_lookup(xfers_data
, xfer
);
1024 /* this is so the prpl can keep the connection open
1025 if it needs to for some odd reason. */
1027 if (xfer
->watcher
) {
1028 purple_input_remove(xfer
->watcher
);
1035 s
= s
- priv
->buffer
->len
;
1038 if (ui_ops
&& ui_ops
->ui_read
) {
1039 gssize tmp
= ui_ops
->ui_read(xfer
, &buffer
, s
);
1042 * UI isn't ready to send data. It will call
1043 * purple_xfer_ui_ready when ready, which sets back up this
1046 if (xfer
->watcher
!= 0) {
1047 purple_input_remove(xfer
->watcher
);
1051 * if we requested 0 bytes it's only normal that en up here
1052 * we shouldn't return as we still have something to
1053 * write in priv->buffer
1057 } else if (tmp
< 0) {
1058 purple_debug_error("filetransfer", "Unable to read whole buffer.\n");
1059 purple_xfer_cancel_local(xfer
);
1065 buffer
= g_malloc0(s
);
1066 result
= fread(buffer
, 1, s
, xfer
->dest_fp
);
1068 purple_debug_error("filetransfer", "Unable to read whole buffer.\n");
1069 purple_xfer_cancel_local(xfer
);
1076 priv
->buffer
= g_byte_array_append(priv
->buffer
, buffer
, result
);
1078 buffer
= priv
->buffer
->data
;
1079 result
= priv
->buffer
->len
;
1082 r
= purple_xfer_write(xfer
, buffer
, result
);
1085 purple_xfer_cancel_remote(xfer
);
1088 } else if (r
== result
) {
1090 * We managed to write the entire buffer. This means our
1091 * network is fast and our buffer is too small, so make it
1094 purple_xfer_increase_buffer_size(xfer
);
1096 if (ui_ops
&& ui_ops
->data_not_sent
)
1097 ui_ops
->data_not_sent(xfer
, buffer
+ r
, result
-r
);
1102 * Remove what we wrote
1103 * If we wrote the whole buffer the byte array will be empty
1104 * Otherwise we'll kee what wasn't sent for next time.
1107 priv
->buffer
= g_byte_array_remove_range(priv
->buffer
, 0, r
);
1112 if (purple_xfer_get_size(xfer
) > 0)
1113 xfer
->bytes_remaining
-= r
;
1115 xfer
->bytes_sent
+= r
;
1117 if (xfer
->ops
.ack
!= NULL
)
1118 xfer
->ops
.ack(xfer
, buffer
, r
);
1122 if (ui_ops
!= NULL
&& ui_ops
->update_progress
!= NULL
)
1123 ui_ops
->update_progress(xfer
,
1124 purple_xfer_get_progress(xfer
));
1127 if ((purple_xfer_get_size(xfer
) > 0) &&
1128 ((purple_xfer_get_bytes_sent(xfer
)) >= purple_xfer_get_size(xfer
))) {
1129 purple_xfer_set_completed(xfer
, TRUE
);
1130 purple_xfer_end(xfer
);
1135 transfer_cb(gpointer data
, gint source
, PurpleInputCondition condition
)
1137 PurpleXfer
*xfer
= data
;
1139 if (xfer
->dest_fp
== NULL
) {
1140 /* The UI is moderating its side manually */
1141 PurpleXferPrivData
*priv
= g_hash_table_lookup(xfers_data
, xfer
);
1142 if (0 == (priv
->ready
& PURPLE_XFER_READY_UI
)) {
1143 priv
->ready
|= PURPLE_XFER_READY_PRPL
;
1145 purple_input_remove(xfer
->watcher
);
1155 begin_transfer(PurpleXfer
*xfer
, PurpleInputCondition cond
)
1157 PurpleXferType type
= purple_xfer_get_type(xfer
);
1158 PurpleXferUiOps
*ui_ops
= purple_xfer_get_ui_ops(xfer
);
1160 if (ui_ops
== NULL
|| (ui_ops
->ui_read
== NULL
&& ui_ops
->ui_write
== NULL
)) {
1161 xfer
->dest_fp
= g_fopen(purple_xfer_get_local_filename(xfer
),
1162 type
== PURPLE_XFER_RECEIVE
? "wb" : "rb");
1164 if (xfer
->dest_fp
== NULL
) {
1165 purple_xfer_show_file_error(xfer
, purple_xfer_get_local_filename(xfer
));
1166 purple_xfer_cancel_local(xfer
);
1170 fseek(xfer
->dest_fp
, xfer
->bytes_sent
, SEEK_SET
);
1174 xfer
->watcher
= purple_input_add(xfer
->fd
, cond
, transfer_cb
, xfer
);
1176 xfer
->start_time
= time(NULL
);
1178 if (xfer
->ops
.start
!= NULL
)
1179 xfer
->ops
.start(xfer
);
1183 connect_cb(gpointer data
, gint source
, const gchar
*error_message
)
1185 PurpleXfer
*xfer
= (PurpleXfer
*)data
;
1188 purple_xfer_cancel_local(xfer
);
1194 begin_transfer(xfer
, PURPLE_INPUT_READ
);
1198 purple_xfer_ui_ready(PurpleXfer
*xfer
)
1200 PurpleInputCondition cond
;
1201 PurpleXferType type
;
1202 PurpleXferPrivData
*priv
;
1204 g_return_if_fail(xfer
!= NULL
);
1206 priv
= g_hash_table_lookup(xfers_data
, xfer
);
1207 priv
->ready
|= PURPLE_XFER_READY_UI
;
1209 if (0 == (priv
->ready
& PURPLE_XFER_READY_PRPL
))
1212 type
= purple_xfer_get_type(xfer
);
1213 if (type
== PURPLE_XFER_SEND
)
1214 cond
= PURPLE_INPUT_WRITE
;
1215 else /* if (type == PURPLE_XFER_RECEIVE) */
1216 cond
= PURPLE_INPUT_READ
;
1218 if (xfer
->watcher
== 0 && xfer
->fd
!= -1)
1219 xfer
->watcher
= purple_input_add(xfer
->fd
, cond
, transfer_cb
, xfer
);
1221 priv
->ready
= PURPLE_XFER_READY_NONE
;
1227 purple_xfer_prpl_ready(PurpleXfer
*xfer
)
1229 PurpleXferPrivData
*priv
;
1231 g_return_if_fail(xfer
!= NULL
);
1233 priv
= g_hash_table_lookup(xfers_data
, xfer
);
1234 priv
->ready
|= PURPLE_XFER_READY_PRPL
;
1236 /* I don't think fwrite/fread are ever *not* ready */
1237 if (xfer
->dest_fp
== NULL
&& 0 == (priv
->ready
& PURPLE_XFER_READY_UI
))
1240 priv
->ready
= PURPLE_XFER_READY_NONE
;
1246 purple_xfer_start(PurpleXfer
*xfer
, int fd
, const char *ip
,
1249 PurpleInputCondition cond
;
1250 PurpleXferType type
;
1252 g_return_if_fail(xfer
!= NULL
);
1253 g_return_if_fail(purple_xfer_get_type(xfer
) != PURPLE_XFER_UNKNOWN
);
1255 type
= purple_xfer_get_type(xfer
);
1257 purple_xfer_set_status(xfer
, PURPLE_XFER_STATUS_STARTED
);
1260 * FIXME 3.0.0 -- there's too much broken code depending on fd == 0
1261 * meaning "don't use a real fd"
1266 if (type
== PURPLE_XFER_RECEIVE
) {
1267 cond
= PURPLE_INPUT_READ
;
1270 xfer
->remote_ip
= g_strdup(ip
);
1271 xfer
->remote_port
= port
;
1273 /* Establish a file descriptor. */
1274 purple_proxy_connect(NULL
, xfer
->account
, xfer
->remote_ip
,
1275 xfer
->remote_port
, connect_cb
, xfer
);
1284 cond
= PURPLE_INPUT_WRITE
;
1289 begin_transfer(xfer
, cond
);
1293 purple_xfer_end(PurpleXfer
*xfer
)
1295 g_return_if_fail(xfer
!= NULL
);
1297 /* See if we are actually trying to cancel this. */
1298 if (!purple_xfer_is_completed(xfer
)) {
1299 purple_xfer_cancel_local(xfer
);
1303 xfer
->end_time
= time(NULL
);
1304 if (xfer
->ops
.end
!= NULL
)
1305 xfer
->ops
.end(xfer
);
1307 if (xfer
->watcher
!= 0) {
1308 purple_input_remove(xfer
->watcher
);
1315 if (xfer
->dest_fp
!= NULL
) {
1316 fclose(xfer
->dest_fp
);
1317 xfer
->dest_fp
= NULL
;
1320 purple_xfer_unref(xfer
);
1324 purple_xfer_add(PurpleXfer
*xfer
)
1326 PurpleXferUiOps
*ui_ops
;
1328 g_return_if_fail(xfer
!= NULL
);
1330 ui_ops
= purple_xfer_get_ui_ops(xfer
);
1332 if (ui_ops
!= NULL
&& ui_ops
->add_xfer
!= NULL
)
1333 ui_ops
->add_xfer(xfer
);
1337 purple_xfer_cancel_local(PurpleXfer
*xfer
)
1339 PurpleXferUiOps
*ui_ops
;
1342 g_return_if_fail(xfer
!= NULL
);
1344 purple_xfer_set_status(xfer
, PURPLE_XFER_STATUS_CANCEL_LOCAL
);
1345 xfer
->end_time
= time(NULL
);
1347 if (purple_xfer_get_filename(xfer
) != NULL
)
1349 msg
= g_strdup_printf(_("You cancelled the transfer of %s"),
1350 purple_xfer_get_filename(xfer
));
1354 msg
= g_strdup(_("File transfer cancelled"));
1356 purple_xfer_conversation_write(xfer
, msg
, FALSE
);
1359 if (purple_xfer_get_type(xfer
) == PURPLE_XFER_SEND
)
1361 if (xfer
->ops
.cancel_send
!= NULL
)
1362 xfer
->ops
.cancel_send(xfer
);
1366 if (xfer
->ops
.cancel_recv
!= NULL
)
1367 xfer
->ops
.cancel_recv(xfer
);
1370 if (xfer
->watcher
!= 0) {
1371 purple_input_remove(xfer
->watcher
);
1378 if (xfer
->dest_fp
!= NULL
) {
1379 fclose(xfer
->dest_fp
);
1380 xfer
->dest_fp
= NULL
;
1383 ui_ops
= purple_xfer_get_ui_ops(xfer
);
1385 if (ui_ops
!= NULL
&& ui_ops
->cancel_local
!= NULL
)
1386 ui_ops
->cancel_local(xfer
);
1388 xfer
->bytes_remaining
= 0;
1390 purple_xfer_unref(xfer
);
1394 purple_xfer_cancel_remote(PurpleXfer
*xfer
)
1396 PurpleXferUiOps
*ui_ops
;
1398 PurpleAccount
*account
;
1401 g_return_if_fail(xfer
!= NULL
);
1403 purple_request_close_with_handle(xfer
);
1404 purple_xfer_set_status(xfer
, PURPLE_XFER_STATUS_CANCEL_REMOTE
);
1405 xfer
->end_time
= time(NULL
);
1407 account
= purple_xfer_get_account(xfer
);
1408 buddy
= purple_find_buddy(account
, xfer
->who
);
1410 if (purple_xfer_get_filename(xfer
) != NULL
)
1412 msg
= g_strdup_printf(_("%s cancelled the transfer of %s"),
1413 buddy
? purple_buddy_get_alias(buddy
) : xfer
->who
, purple_xfer_get_filename(xfer
));
1417 msg
= g_strdup_printf(_("%s cancelled the file transfer"),
1418 buddy
? purple_buddy_get_alias(buddy
) : xfer
->who
);
1420 purple_xfer_conversation_write(xfer
, msg
, TRUE
);
1421 purple_xfer_error(purple_xfer_get_type(xfer
), account
, xfer
->who
, msg
);
1424 if (purple_xfer_get_type(xfer
) == PURPLE_XFER_SEND
)
1426 if (xfer
->ops
.cancel_send
!= NULL
)
1427 xfer
->ops
.cancel_send(xfer
);
1431 if (xfer
->ops
.cancel_recv
!= NULL
)
1432 xfer
->ops
.cancel_recv(xfer
);
1435 if (xfer
->watcher
!= 0) {
1436 purple_input_remove(xfer
->watcher
);
1443 if (xfer
->dest_fp
!= NULL
) {
1444 fclose(xfer
->dest_fp
);
1445 xfer
->dest_fp
= NULL
;
1448 ui_ops
= purple_xfer_get_ui_ops(xfer
);
1450 if (ui_ops
!= NULL
&& ui_ops
->cancel_remote
!= NULL
)
1451 ui_ops
->cancel_remote(xfer
);
1453 xfer
->bytes_remaining
= 0;
1455 purple_xfer_unref(xfer
);
1459 purple_xfer_error(PurpleXferType type
, PurpleAccount
*account
, const char *who
, const char *msg
)
1463 g_return_if_fail(msg
!= NULL
);
1464 g_return_if_fail(type
!= PURPLE_XFER_UNKNOWN
);
1468 buddy
= purple_find_buddy(account
, who
);
1470 who
= purple_buddy_get_alias(buddy
);
1473 if (type
== PURPLE_XFER_SEND
)
1474 title
= g_strdup_printf(_("File transfer to %s failed."), who
);
1476 title
= g_strdup_printf(_("File transfer from %s failed."), who
);
1478 purple_notify_error(NULL
, NULL
, title
, msg
);
1484 purple_xfer_update_progress(PurpleXfer
*xfer
)
1486 PurpleXferUiOps
*ui_ops
;
1488 g_return_if_fail(xfer
!= NULL
);
1490 ui_ops
= purple_xfer_get_ui_ops(xfer
);
1491 if (ui_ops
!= NULL
&& ui_ops
->update_progress
!= NULL
)
1492 ui_ops
->update_progress(xfer
, purple_xfer_get_progress(xfer
));
1496 /**************************************************************************
1497 * File Transfer Subsystem API
1498 **************************************************************************/
1500 purple_xfers_get_handle(void) {
1501 static int handle
= 0;
1507 purple_xfers_init(void) {
1508 void *handle
= purple_xfers_get_handle();
1510 xfers_data
= g_hash_table_new_full(g_direct_hash
, g_direct_equal
,
1511 NULL
, purple_xfer_priv_data_destroy
);
1513 /* register signals */
1514 purple_signal_register(handle
, "file-recv-accept",
1515 purple_marshal_VOID__POINTER
, NULL
, 1,
1516 purple_value_new(PURPLE_TYPE_SUBTYPE
,
1517 PURPLE_SUBTYPE_XFER
));
1518 purple_signal_register(handle
, "file-send-accept",
1519 purple_marshal_VOID__POINTER
, NULL
, 1,
1520 purple_value_new(PURPLE_TYPE_SUBTYPE
,
1521 PURPLE_SUBTYPE_XFER
));
1522 purple_signal_register(handle
, "file-recv-start",
1523 purple_marshal_VOID__POINTER
, NULL
, 1,
1524 purple_value_new(PURPLE_TYPE_SUBTYPE
,
1525 PURPLE_SUBTYPE_XFER
));
1526 purple_signal_register(handle
, "file-send-start",
1527 purple_marshal_VOID__POINTER
, NULL
, 1,
1528 purple_value_new(PURPLE_TYPE_SUBTYPE
,
1529 PURPLE_SUBTYPE_XFER
));
1530 purple_signal_register(handle
, "file-send-cancel",
1531 purple_marshal_VOID__POINTER
, NULL
, 1,
1532 purple_value_new(PURPLE_TYPE_SUBTYPE
,
1533 PURPLE_SUBTYPE_XFER
));
1534 purple_signal_register(handle
, "file-recv-cancel",
1535 purple_marshal_VOID__POINTER
, NULL
, 1,
1536 purple_value_new(PURPLE_TYPE_SUBTYPE
,
1537 PURPLE_SUBTYPE_XFER
));
1538 purple_signal_register(handle
, "file-send-complete",
1539 purple_marshal_VOID__POINTER
, NULL
, 1,
1540 purple_value_new(PURPLE_TYPE_SUBTYPE
,
1541 PURPLE_SUBTYPE_XFER
));
1542 purple_signal_register(handle
, "file-recv-complete",
1543 purple_marshal_VOID__POINTER
, NULL
, 1,
1544 purple_value_new(PURPLE_TYPE_SUBTYPE
,
1545 PURPLE_SUBTYPE_XFER
));
1546 purple_signal_register(handle
, "file-recv-request",
1547 purple_marshal_VOID__POINTER
, NULL
, 1,
1548 purple_value_new(PURPLE_TYPE_SUBTYPE
,
1549 PURPLE_SUBTYPE_XFER
));
1553 purple_xfers_uninit(void)
1555 void *handle
= purple_xfers_get_handle();
1557 purple_signals_disconnect_by_handle(handle
);
1558 purple_signals_unregister_by_instance(handle
);
1560 g_hash_table_destroy(xfers_data
);
1565 purple_xfers_set_ui_ops(PurpleXferUiOps
*ops
) {
1570 purple_xfers_get_ui_ops(void) {