3 * Purple is the legal property of its developers, whose names are too numerous
4 * to list here. Please refer to the COPYRIGHT file distributed with this
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
23 #include "glibcompat.h"
26 #include "image-store.h"
36 #define FT_INITIAL_BUFFER_SIZE 4096
37 #define FT_MAX_BUFFER_SIZE 65535
39 typedef struct _PurpleXferPrivate PurpleXferPrivate
;
41 static PurpleXferUiOps
*xfer_ui_ops
= NULL
;
44 /* Private data for a file transfer */
45 struct _PurpleXferPrivate
{
46 PurpleXferType type
; /* The type of transfer. */
48 PurpleAccount
*account
; /* The account. */
50 char *who
; /* The person on the other end of the
53 char *message
; /* A message sent with the request */
54 char *filename
; /* The name sent over the network. */
55 char *local_filename
; /* The name on the local hard drive. */
56 goffset size
; /* The size of the file. */
58 FILE *dest_fp
; /* The destination file pointer. */
60 char *remote_ip
; /* The remote IP address. */
61 guint16 local_port
; /* The local port. */
62 guint16 remote_port
; /* The remote port. */
64 int fd
; /* The socket file descriptor. */
65 int watcher
; /* Watcher. */
67 goffset bytes_sent
; /* The number of bytes sent. */
68 gint64 start_time
; /* When the transfer of data began. */
69 gint64 end_time
; /* When the transfer of data ended. */
71 size_t current_buffer_size
; /* This gradually increases for fast
72 network connections. */
74 PurpleXferStatus status
; /* File Transfer's status. */
76 gpointer ui_data
; /* UI-specific data */
77 PurpleXferUiOps
*ui_ops
; /* UI-specific operations. */
80 * Used to moderate the file transfer when either the read/write ui_ops are
81 * set or fd is not set. In those cases, the UI/protocol call the respective
82 * function, which is somewhat akin to a fd watch being triggered.
85 PURPLE_XFER_READY_NONE
= 0x0,
86 PURPLE_XFER_READY_UI
= 0x1,
87 PURPLE_XFER_READY_PROTOCOL
= 0x2,
90 /* TODO: Should really use a PurpleCircBuffer for this. */
93 gpointer thumbnail_data
; /* thumbnail image */
95 gchar
*thumbnail_mimetype
;
98 /* GObject property enums */
122 static GParamSpec
*properties
[PROP_LAST
];
124 G_DEFINE_TYPE_WITH_PRIVATE(PurpleXfer
, purple_xfer
, G_TYPE_OBJECT
);
126 static int purple_xfer_choose_file(PurpleXfer
*xfer
);
129 purple_xfer_status_type_to_string(PurpleXferStatus type
)
131 static const struct {
132 PurpleXferStatus type
;
135 { PURPLE_XFER_STATUS_UNKNOWN
, "unknown" },
136 { PURPLE_XFER_STATUS_NOT_STARTED
, "not started" },
137 { PURPLE_XFER_STATUS_ACCEPTED
, "accepted" },
138 { PURPLE_XFER_STATUS_STARTED
, "started" },
139 { PURPLE_XFER_STATUS_DONE
, "done" },
140 { PURPLE_XFER_STATUS_CANCEL_LOCAL
, "cancelled locally" },
141 { PURPLE_XFER_STATUS_CANCEL_REMOTE
, "cancelled remotely" }
145 for (i
= 0; i
< G_N_ELEMENTS(type_names
); ++i
)
146 if (type_names
[i
].type
== type
)
147 return type_names
[i
].name
;
149 return "invalid state";
153 purple_xfer_set_status(PurpleXfer
*xfer
, PurpleXferStatus status
)
155 PurpleXferPrivate
*priv
= NULL
;
157 g_return_if_fail(PURPLE_IS_XFER(xfer
));
159 priv
= purple_xfer_get_instance_private(xfer
);
161 if (purple_debug_is_verbose())
162 purple_debug_info("xfer", "Changing status of xfer %p from %s to %s\n",
163 xfer
, purple_xfer_status_type_to_string(priv
->status
),
164 purple_xfer_status_type_to_string(status
));
166 if (priv
->status
== status
)
169 priv
->status
= status
;
171 g_object_notify_by_pspec(G_OBJECT(xfer
), properties
[PROP_STATUS
]);
173 if(priv
->type
== PURPLE_XFER_TYPE_SEND
) {
175 case PURPLE_XFER_STATUS_ACCEPTED
:
176 purple_signal_emit(purple_xfers_get_handle(), "file-send-accept", xfer
);
178 case PURPLE_XFER_STATUS_STARTED
:
179 purple_signal_emit(purple_xfers_get_handle(), "file-send-start", xfer
);
181 case PURPLE_XFER_STATUS_DONE
:
182 purple_signal_emit(purple_xfers_get_handle(), "file-send-complete", xfer
);
184 case PURPLE_XFER_STATUS_CANCEL_LOCAL
:
185 case PURPLE_XFER_STATUS_CANCEL_REMOTE
:
186 purple_signal_emit(purple_xfers_get_handle(), "file-send-cancel", xfer
);
191 } else if(priv
->type
== PURPLE_XFER_TYPE_RECEIVE
) {
193 case PURPLE_XFER_STATUS_ACCEPTED
:
194 purple_signal_emit(purple_xfers_get_handle(), "file-recv-accept", xfer
);
196 case PURPLE_XFER_STATUS_STARTED
:
197 purple_signal_emit(purple_xfers_get_handle(), "file-recv-start", xfer
);
199 case PURPLE_XFER_STATUS_DONE
:
200 purple_signal_emit(purple_xfers_get_handle(), "file-recv-complete", xfer
);
202 case PURPLE_XFER_STATUS_CANCEL_LOCAL
:
203 case PURPLE_XFER_STATUS_CANCEL_REMOTE
:
204 purple_signal_emit(purple_xfers_get_handle(), "file-recv-cancel", xfer
);
213 purple_xfer_conversation_write_internal(PurpleXfer
*xfer
,
214 const char *message
, gboolean is_error
, gboolean print_thumbnail
)
216 PurpleIMConversation
*im
= NULL
;
217 PurpleMessageFlags flags
= PURPLE_MESSAGE_SYSTEM
;
219 gconstpointer thumbnail_data
;
221 PurpleXferPrivate
*priv
= purple_xfer_get_instance_private(xfer
);
223 thumbnail_data
= purple_xfer_get_thumbnail(xfer
, &size
);
225 im
= purple_conversations_find_im_with_account(priv
->who
,
226 purple_xfer_get_account(xfer
));
231 escaped
= g_markup_escape_text(message
, -1);
234 flags
|= PURPLE_MESSAGE_ERROR
;
236 if (print_thumbnail
&& thumbnail_data
) {
237 gchar
*message_with_img
;
238 gpointer data
= g_memdup(thumbnail_data
, size
);
242 img
= purple_image_new_from_data(data
, size
);
243 id
= purple_image_store_add(img
);
246 message_with_img
= g_strdup_printf("<img src=\""
247 PURPLE_IMAGE_STORE_PROTOCOL
"%u\"> %s", id
, escaped
);
248 purple_conversation_write_system_message(
249 PURPLE_CONVERSATION(im
), message_with_img
, flags
);
250 g_free(message_with_img
);
252 purple_conversation_write_system_message(
253 PURPLE_CONVERSATION(im
), escaped
, flags
);
259 purple_xfer_conversation_write(PurpleXfer
*xfer
, const gchar
*message
,
262 g_return_if_fail(PURPLE_IS_XFER(xfer
));
263 g_return_if_fail(message
!= NULL
);
265 purple_xfer_conversation_write_internal(xfer
, message
, is_error
, FALSE
);
268 /* maybe this one should be exported publically? */
270 purple_xfer_conversation_write_with_thumbnail(PurpleXfer
*xfer
,
271 const gchar
*message
)
273 purple_xfer_conversation_write_internal(xfer
, message
, FALSE
, TRUE
);
277 static void purple_xfer_show_file_error(PurpleXfer
*xfer
, const char *filename
)
280 gchar
*msg
= NULL
, *utf8
;
281 PurpleXferType xfer_type
= purple_xfer_get_xfer_type(xfer
);
282 PurpleAccount
*account
= purple_xfer_get_account(xfer
);
283 PurpleXferPrivate
*priv
= purple_xfer_get_instance_private(xfer
);
285 utf8
= g_filename_to_utf8(filename
, -1, NULL
, NULL
, NULL
);
287 case PURPLE_XFER_TYPE_SEND
:
288 msg
= g_strdup_printf(_("Error reading %s: \n%s.\n"),
289 utf8
, g_strerror(err
));
291 case PURPLE_XFER_TYPE_RECEIVE
:
292 msg
= g_strdup_printf(_("Error writing %s: \n%s.\n"),
293 utf8
, g_strerror(err
));
296 msg
= g_strdup_printf(_("Error accessing %s: \n%s.\n"),
297 utf8
, g_strerror(err
));
302 purple_xfer_conversation_write(xfer
, msg
, TRUE
);
303 purple_xfer_error(xfer_type
, account
, priv
->who
, msg
);
308 purple_xfer_choose_file_ok_cb(void *user_data
, const char *filename
)
315 xfer
= (PurpleXfer
*)user_data
;
316 type
= purple_xfer_get_xfer_type(xfer
);
318 if (g_stat(filename
, &st
) != 0) {
319 /* File not found. */
320 if (type
== PURPLE_XFER_TYPE_RECEIVE
) {
326 dir
= g_path_get_dirname(filename
);
328 if (g_access(dir
, mode
) == 0) {
329 purple_xfer_request_accepted(xfer
, filename
);
332 purple_notify_message(
333 NULL
, PURPLE_NOTIFY_MSG_ERROR
, NULL
,
334 _("Directory is not writable."), NULL
,
335 purple_request_cpar_from_account(
336 purple_xfer_get_account(xfer
)),
337 (PurpleNotifyCloseCallback
)purple_xfer_choose_file
, xfer
);
343 purple_xfer_show_file_error(xfer
, filename
);
344 purple_xfer_cancel_local(xfer
);
347 else if ((type
== PURPLE_XFER_TYPE_SEND
) && (st
.st_size
== 0)) {
349 purple_notify_error(NULL
, NULL
,
350 _("Cannot send a file of 0 bytes."), NULL
,
351 purple_request_cpar_from_account(
352 purple_xfer_get_account(xfer
)));
354 purple_xfer_cancel_local(xfer
);
356 else if ((type
== PURPLE_XFER_TYPE_SEND
) && S_ISDIR(st
.st_mode
)) {
358 * XXX - Sending a directory should be valid for some protocols.
360 purple_notify_error(NULL
, NULL
, _("Cannot send a directory."),
361 NULL
, purple_request_cpar_from_account(
362 purple_xfer_get_account(xfer
)));
364 purple_xfer_cancel_local(xfer
);
366 else if ((type
== PURPLE_XFER_TYPE_RECEIVE
) && S_ISDIR(st
.st_mode
)) {
368 utf8
= g_filename_to_utf8(filename
, -1, NULL
, NULL
, NULL
);
369 msg
= g_strdup_printf(
370 _("%s is not a regular file. Cowardly refusing to overwrite it.\n"), utf8
);
372 purple_notify_error(NULL
, NULL
, msg
, NULL
,
373 purple_request_cpar_from_account(
374 purple_xfer_get_account(xfer
)));
376 purple_xfer_request_denied(xfer
);
378 else if (type
== PURPLE_XFER_TYPE_SEND
) {
385 if (g_access(filename
, mode
) == 0) {
386 purple_xfer_request_accepted(xfer
, filename
);
389 purple_notify_message(
390 NULL
, PURPLE_NOTIFY_MSG_ERROR
, NULL
,
391 _("File is not readable."), NULL
,
392 purple_request_cpar_from_account(
393 purple_xfer_get_account(xfer
)),
394 (PurpleNotifyCloseCallback
)purple_xfer_choose_file
, xfer
);
398 purple_xfer_request_accepted(xfer
, filename
);
401 g_object_unref(xfer
);
405 purple_xfer_choose_file_cancel_cb(void *user_data
, const char *filename
)
407 PurpleXfer
*xfer
= (PurpleXfer
*)user_data
;
409 purple_xfer_set_status(xfer
, PURPLE_XFER_STATUS_CANCEL_LOCAL
);
410 if (purple_xfer_get_xfer_type(xfer
) == PURPLE_XFER_TYPE_SEND
)
411 purple_xfer_cancel_local(xfer
);
413 purple_xfer_request_denied(xfer
);
414 g_object_unref(xfer
);
418 purple_xfer_choose_file(PurpleXfer
*xfer
)
420 purple_request_file(xfer
, NULL
, purple_xfer_get_filename(xfer
),
421 (purple_xfer_get_xfer_type(xfer
) == PURPLE_XFER_TYPE_RECEIVE
),
422 G_CALLBACK(purple_xfer_choose_file_ok_cb
),
423 G_CALLBACK(purple_xfer_choose_file_cancel_cb
),
424 purple_request_cpar_from_account(purple_xfer_get_account(xfer
)),
431 cancel_recv_cb(PurpleXfer
*xfer
)
433 purple_xfer_set_status(xfer
, PURPLE_XFER_STATUS_CANCEL_LOCAL
);
434 purple_xfer_request_denied(xfer
);
435 g_object_unref(xfer
);
441 purple_xfer_ask_recv(PurpleXfer
*xfer
)
443 PurpleXferPrivate
*priv
= purple_xfer_get_instance_private(xfer
);
444 char *buf
, *size_buf
;
449 /* If we have already accepted the request, ask the destination file
451 if (purple_xfer_get_status(xfer
) != PURPLE_XFER_STATUS_ACCEPTED
) {
452 PurpleRequestCommonParameters
*cpar
;
453 PurpleBuddy
*buddy
= purple_blist_find_buddy(priv
->account
, priv
->who
);
455 if (purple_xfer_get_filename(xfer
) != NULL
)
457 size
= purple_xfer_get_size(xfer
);
458 size_buf
= g_format_size_full(size
, G_FORMAT_SIZE_LONG_FORMAT
);
459 buf
= g_strdup_printf(_("%s wants to send you %s (%s)"),
460 buddy
? purple_buddy_get_alias(buddy
) : priv
->who
,
461 purple_xfer_get_filename(xfer
), size_buf
);
466 buf
= g_strdup_printf(_("%s wants to send you a file"),
467 buddy
? purple_buddy_get_alias(buddy
) : priv
->who
);
470 if (priv
->message
!= NULL
)
471 purple_serv_got_im(purple_account_get_connection(priv
->account
),
472 priv
->who
, priv
->message
, 0, time(NULL
));
474 cpar
= purple_request_cpar_from_account(priv
->account
);
475 if ((thumb
= purple_xfer_get_thumbnail(xfer
, &thumb_size
))) {
476 purple_request_cpar_set_custom_icon(cpar
, thumb
,
480 purple_request_accept_cancel(xfer
, NULL
, buf
, NULL
,
481 PURPLE_DEFAULT_ACTION_NONE
, cpar
, xfer
,
482 G_CALLBACK(purple_xfer_choose_file
),
483 G_CALLBACK(cancel_recv_cb
));
487 purple_xfer_choose_file(xfer
);
491 ask_accept_ok(PurpleXfer
*xfer
)
493 purple_xfer_request_accepted(xfer
, NULL
);
499 ask_accept_cancel(PurpleXfer
*xfer
)
501 purple_xfer_request_denied(xfer
);
502 g_object_unref(xfer
);
508 purple_xfer_ask_accept(PurpleXfer
*xfer
)
510 PurpleXferPrivate
*priv
= purple_xfer_get_instance_private(xfer
);
511 char *buf
, *buf2
= NULL
;
512 PurpleBuddy
*buddy
= purple_blist_find_buddy(priv
->account
, priv
->who
);
514 buf
= g_strdup_printf(_("Accept file transfer request from %s?"),
515 buddy
? purple_buddy_get_alias(buddy
) : priv
->who
);
516 if (purple_xfer_get_remote_ip(xfer
) &&
517 purple_xfer_get_remote_port(xfer
))
518 buf2
= g_strdup_printf(_("A file is available for download from:\n"
519 "Remote host: %s\nRemote port: %d"),
520 purple_xfer_get_remote_ip(xfer
),
521 purple_xfer_get_remote_port(xfer
));
522 purple_request_accept_cancel(xfer
, NULL
, buf
, buf2
,
523 PURPLE_DEFAULT_ACTION_NONE
,
524 purple_request_cpar_from_account(priv
->account
), xfer
,
525 G_CALLBACK(ask_accept_ok
), G_CALLBACK(ask_accept_cancel
));
531 purple_xfer_request(PurpleXfer
*xfer
)
533 PurpleXferPrivate
*priv
= NULL
;
535 g_return_if_fail(PURPLE_IS_XFER(xfer
));
537 /* this is unref'd in the finishers, like cancel and stop */
540 priv
= purple_xfer_get_instance_private(xfer
);
542 if (priv
->type
== PURPLE_XFER_TYPE_RECEIVE
)
544 purple_signal_emit(purple_xfers_get_handle(), "file-recv-request", xfer
);
545 if (purple_xfer_get_status(xfer
) == PURPLE_XFER_STATUS_CANCEL_LOCAL
)
547 /* The file-transfer was cancelled by a plugin */
548 purple_xfer_cancel_local(xfer
);
550 else if (priv
->filename
|| priv
->status
== PURPLE_XFER_STATUS_ACCEPTED
)
552 gchar
* message
= NULL
;
553 PurpleBuddy
*buddy
= purple_blist_find_buddy(priv
->account
, priv
->who
);
555 message
= g_strdup_printf(_("%s is offering to send file %s"),
556 buddy
? purple_buddy_get_alias(buddy
) : priv
->who
, purple_xfer_get_filename(xfer
));
557 purple_xfer_conversation_write_with_thumbnail(xfer
, message
);
560 /* Ask for a filename to save to if it's not already given by a plugin */
561 if (priv
->local_filename
== NULL
)
562 purple_xfer_ask_recv(xfer
);
566 purple_xfer_ask_accept(xfer
);
571 purple_xfer_choose_file(xfer
);
576 purple_xfer_request_accepted(PurpleXfer
*xfer
, const gchar
*filename
)
578 PurpleXferClass
*klass
= NULL
;
579 PurpleXferPrivate
*priv
= NULL
;
581 gchar
*msg
, *utf8
, *base
;
584 g_return_if_fail(PURPLE_IS_XFER(xfer
));
586 klass
= PURPLE_XFER_GET_CLASS(xfer
);
587 priv
= purple_xfer_get_instance_private(xfer
);
589 purple_debug_misc("xfer", "request accepted for %p\n", xfer
);
591 if(filename
== NULL
) {
592 if(priv
->type
== PURPLE_XFER_TYPE_RECEIVE
) {
593 purple_xfer_set_status(xfer
, PURPLE_XFER_STATUS_ACCEPTED
);
596 purple_debug_warning(
598 "can not set file transfer without a file name"
605 buddy
= purple_blist_find_buddy(priv
->account
, priv
->who
);
607 if (priv
->type
== PURPLE_XFER_TYPE_SEND
) {
609 /* Check the filename. */
610 PurpleXferUiOps
*ui_ops
;
611 ui_ops
= purple_xfer_get_ui_ops(xfer
);
614 if (g_strrstr(filename
, "../") || g_strrstr(filename
, "..\\"))
616 if (g_strrstr(filename
, "../"))
619 utf8
= g_filename_to_utf8(filename
, -1, NULL
, NULL
, NULL
);
621 msg
= g_strdup_printf(_("%s is not a valid filename.\n"), utf8
);
622 purple_xfer_error(priv
->type
, priv
->account
, priv
->who
, msg
);
626 g_object_unref(xfer
);
630 if (ui_ops
== NULL
|| (ui_ops
->ui_read
== NULL
&& ui_ops
->ui_write
== NULL
)) {
631 if (g_stat(filename
, &st
) == -1) {
632 purple_xfer_show_file_error(xfer
, filename
);
633 g_object_unref(xfer
);
637 purple_xfer_set_local_filename(xfer
, filename
);
638 purple_xfer_set_size(xfer
, st
.st_size
);
640 purple_xfer_set_local_filename(xfer
, filename
);
643 base
= g_path_get_basename(filename
);
644 utf8
= g_filename_to_utf8(base
, -1, NULL
, NULL
, NULL
);
646 purple_xfer_set_filename(xfer
, utf8
);
648 msg
= g_strdup_printf(_("Offering to send %s to %s"),
649 utf8
, buddy
? purple_buddy_get_alias(buddy
) : priv
->who
);
651 purple_xfer_conversation_write(xfer
, msg
, FALSE
);
655 /* Receiving a file */
656 purple_xfer_set_status(xfer
, PURPLE_XFER_STATUS_ACCEPTED
);
657 purple_xfer_set_local_filename(xfer
, filename
);
659 msg
= g_strdup_printf(_("Starting transfer of %s from %s"),
660 priv
->filename
, buddy
? purple_buddy_get_alias(buddy
) : priv
->who
);
661 purple_xfer_conversation_write(xfer
, msg
, FALSE
);
665 purple_xfer_add(xfer
);
671 purple_xfer_request_denied(PurpleXfer
*xfer
)
673 PurpleXferClass
*klass
= NULL
;
675 g_return_if_fail(PURPLE_XFER(xfer
));
677 klass
= PURPLE_XFER_GET_CLASS(xfer
);
679 purple_debug_misc("xfer", "xfer %p denied\n", xfer
);
681 if(klass
&& klass
->request_denied
) {
682 klass
->request_denied(xfer
);
685 g_object_unref(xfer
);
688 int purple_xfer_get_fd(PurpleXfer
*xfer
)
690 PurpleXferPrivate
*priv
= NULL
;
692 g_return_val_if_fail(PURPLE_IS_XFER(xfer
), 0);
694 priv
= purple_xfer_get_instance_private(xfer
);
698 int purple_xfer_get_watcher(PurpleXfer
*xfer
)
700 PurpleXferPrivate
*priv
= NULL
;
702 g_return_val_if_fail(PURPLE_IS_XFER(xfer
), 0);
704 priv
= purple_xfer_get_instance_private(xfer
);
705 return priv
->watcher
;
709 purple_xfer_get_xfer_type(PurpleXfer
*xfer
)
711 PurpleXferPrivate
*priv
= NULL
;
713 g_return_val_if_fail(PURPLE_IS_XFER(xfer
), PURPLE_XFER_TYPE_UNKNOWN
);
715 priv
= purple_xfer_get_instance_private(xfer
);
720 purple_xfer_get_account(PurpleXfer
*xfer
)
722 PurpleXferPrivate
*priv
= NULL
;
724 g_return_val_if_fail(PURPLE_IS_XFER(xfer
), NULL
);
726 priv
= purple_xfer_get_instance_private(xfer
);
727 return priv
->account
;
731 purple_xfer_set_remote_user(PurpleXfer
*xfer
, const char *who
)
733 PurpleXferPrivate
*priv
= NULL
;
735 g_return_if_fail(PURPLE_IS_XFER(xfer
));
737 priv
= purple_xfer_get_instance_private(xfer
);
739 priv
->who
= g_strdup(who
);
741 g_object_notify_by_pspec(G_OBJECT(xfer
), properties
[PROP_REMOTE_USER
]);
745 purple_xfer_get_remote_user(PurpleXfer
*xfer
)
747 PurpleXferPrivate
*priv
= NULL
;
749 g_return_val_if_fail(PURPLE_IS_XFER(xfer
), NULL
);
751 priv
= purple_xfer_get_instance_private(xfer
);
756 purple_xfer_get_status(PurpleXfer
*xfer
)
758 PurpleXferPrivate
*priv
= NULL
;
760 g_return_val_if_fail(PURPLE_IS_XFER(xfer
), PURPLE_XFER_STATUS_UNKNOWN
);
762 priv
= purple_xfer_get_instance_private(xfer
);
767 purple_xfer_is_cancelled(PurpleXfer
*xfer
)
769 g_return_val_if_fail(PURPLE_IS_XFER(xfer
), TRUE
);
771 if ((purple_xfer_get_status(xfer
) == PURPLE_XFER_STATUS_CANCEL_LOCAL
) ||
772 (purple_xfer_get_status(xfer
) == PURPLE_XFER_STATUS_CANCEL_REMOTE
))
779 purple_xfer_is_completed(PurpleXfer
*xfer
)
781 g_return_val_if_fail(PURPLE_IS_XFER(xfer
), TRUE
);
783 return (purple_xfer_get_status(xfer
) == PURPLE_XFER_STATUS_DONE
);
787 purple_xfer_get_filename(PurpleXfer
*xfer
)
789 PurpleXferPrivate
*priv
= NULL
;
791 g_return_val_if_fail(PURPLE_IS_XFER(xfer
), NULL
);
793 priv
= purple_xfer_get_instance_private(xfer
);
794 return priv
->filename
;
798 purple_xfer_get_local_filename(PurpleXfer
*xfer
)
800 PurpleXferPrivate
*priv
= NULL
;
802 g_return_val_if_fail(PURPLE_IS_XFER(xfer
), NULL
);
804 priv
= purple_xfer_get_instance_private(xfer
);
805 return priv
->local_filename
;
809 purple_xfer_get_bytes_sent(PurpleXfer
*xfer
)
811 PurpleXferPrivate
*priv
= NULL
;
813 g_return_val_if_fail(PURPLE_IS_XFER(xfer
), 0);
815 priv
= purple_xfer_get_instance_private(xfer
);
816 return priv
->bytes_sent
;
820 purple_xfer_get_bytes_remaining(PurpleXfer
*xfer
)
822 PurpleXferPrivate
*priv
= NULL
;
824 g_return_val_if_fail(PURPLE_IS_XFER(xfer
), 0);
826 priv
= purple_xfer_get_instance_private(xfer
);
827 return priv
->size
- priv
->bytes_sent
;
831 purple_xfer_get_size(PurpleXfer
*xfer
)
833 PurpleXferPrivate
*priv
= NULL
;
835 g_return_val_if_fail(PURPLE_IS_XFER(xfer
), 0);
837 priv
= purple_xfer_get_instance_private(xfer
);
842 purple_xfer_get_progress(PurpleXfer
*xfer
)
844 g_return_val_if_fail(PURPLE_IS_XFER(xfer
), 0.0);
846 if (purple_xfer_get_size(xfer
) == 0)
849 return ((double)purple_xfer_get_bytes_sent(xfer
) /
850 (double)purple_xfer_get_size(xfer
));
854 purple_xfer_get_local_port(PurpleXfer
*xfer
)
856 PurpleXferPrivate
*priv
= NULL
;
858 g_return_val_if_fail(PURPLE_IS_XFER(xfer
), -1);
860 priv
= purple_xfer_get_instance_private(xfer
);
861 return priv
->local_port
;
865 purple_xfer_get_remote_ip(PurpleXfer
*xfer
)
867 PurpleXferPrivate
*priv
= NULL
;
869 g_return_val_if_fail(PURPLE_IS_XFER(xfer
), NULL
);
871 priv
= purple_xfer_get_instance_private(xfer
);
872 return priv
->remote_ip
;
876 purple_xfer_get_remote_port(PurpleXfer
*xfer
)
878 PurpleXferPrivate
*priv
= NULL
;
880 g_return_val_if_fail(PURPLE_IS_XFER(xfer
), -1);
882 priv
= purple_xfer_get_instance_private(xfer
);
883 return priv
->remote_port
;
887 purple_xfer_get_start_time(PurpleXfer
*xfer
)
889 PurpleXferPrivate
*priv
= NULL
;
891 g_return_val_if_fail(PURPLE_IS_XFER(xfer
), 0);
893 priv
= purple_xfer_get_instance_private(xfer
);
894 return priv
->start_time
;
898 purple_xfer_get_end_time(PurpleXfer
*xfer
)
900 PurpleXferPrivate
*priv
= NULL
;
902 g_return_val_if_fail(PURPLE_IS_XFER(xfer
), 0);
904 priv
= purple_xfer_get_instance_private(xfer
);
905 return priv
->end_time
;
908 void purple_xfer_set_fd(PurpleXfer
*xfer
, int fd
)
910 PurpleXferPrivate
*priv
= NULL
;
912 g_return_if_fail(PURPLE_IS_XFER(xfer
));
914 priv
= purple_xfer_get_instance_private(xfer
);
917 g_object_notify_by_pspec(G_OBJECT(xfer
), properties
[PROP_FD
]);
920 void purple_xfer_set_watcher(PurpleXfer
*xfer
, int watcher
)
922 PurpleXferPrivate
*priv
= NULL
;
924 g_return_if_fail(PURPLE_IS_XFER(xfer
));
926 priv
= purple_xfer_get_instance_private(xfer
);
927 priv
->watcher
= watcher
;
929 g_object_notify_by_pspec(G_OBJECT(xfer
), properties
[PROP_WATCHER
]);
933 purple_xfer_set_completed(PurpleXfer
*xfer
, gboolean completed
)
935 PurpleXferPrivate
*priv
= NULL
;
936 PurpleXferUiOps
*ui_ops
;
938 g_return_if_fail(PURPLE_IS_XFER(xfer
));
940 priv
= purple_xfer_get_instance_private(xfer
);
942 if (completed
== TRUE
) {
944 PurpleIMConversation
*im
;
946 purple_xfer_set_status(xfer
, PURPLE_XFER_STATUS_DONE
);
948 if (purple_xfer_get_filename(xfer
) != NULL
)
950 char *filename
= g_markup_escape_text(purple_xfer_get_filename(xfer
), -1);
951 if (purple_xfer_get_local_filename(xfer
)
952 && purple_xfer_get_xfer_type(xfer
) == PURPLE_XFER_TYPE_RECEIVE
)
954 char *local
= g_markup_escape_text(purple_xfer_get_local_filename(xfer
), -1);
955 msg
= g_strdup_printf(_("Transfer of file <A HREF=\"file://%s\">%s</A> complete"),
960 msg
= g_strdup_printf(_("Transfer of file %s complete"),
965 msg
= g_strdup(_("File transfer complete"));
967 im
= purple_conversations_find_im_with_account(priv
->who
,
968 purple_xfer_get_account(xfer
));
971 purple_conversation_write_system_message(
972 PURPLE_CONVERSATION(im
), msg
, 0);
977 ui_ops
= purple_xfer_get_ui_ops(xfer
);
979 if (ui_ops
!= NULL
&& ui_ops
->update_progress
!= NULL
)
980 ui_ops
->update_progress(xfer
, purple_xfer_get_progress(xfer
));
984 purple_xfer_set_message(PurpleXfer
*xfer
, const char *message
)
986 PurpleXferPrivate
*priv
= NULL
;
988 g_return_if_fail(PURPLE_IS_XFER(xfer
));
990 priv
= purple_xfer_get_instance_private(xfer
);
992 if (message
!= priv
->message
) {
993 g_free(priv
->message
);
994 priv
->message
= g_strdup(message
);
997 g_object_notify_by_pspec(G_OBJECT(xfer
), properties
[PROP_MESSAGE
]);
1001 purple_xfer_get_message(PurpleXfer
*xfer
)
1003 PurpleXferPrivate
*priv
= NULL
;
1005 g_return_val_if_fail(PURPLE_IS_XFER(xfer
), NULL
);
1007 priv
= purple_xfer_get_instance_private(xfer
);
1008 return priv
->message
;
1012 purple_xfer_set_filename(PurpleXfer
*xfer
, const char *filename
)
1014 PurpleXferPrivate
*priv
= NULL
;
1016 g_return_if_fail(PURPLE_IS_XFER(xfer
));
1018 priv
= purple_xfer_get_instance_private(xfer
);
1020 if (filename
!= priv
->filename
) {
1021 g_free(priv
->filename
);
1022 priv
->filename
= g_strdup(filename
);
1025 g_object_notify_by_pspec(G_OBJECT(xfer
), properties
[PROP_FILENAME
]);
1029 purple_xfer_set_local_filename(PurpleXfer
*xfer
, const char *filename
)
1031 PurpleXferPrivate
*priv
= NULL
;
1033 g_return_if_fail(PURPLE_IS_XFER(xfer
));
1035 priv
= purple_xfer_get_instance_private(xfer
);
1037 if (filename
!= priv
->local_filename
) {
1038 g_free(priv
->local_filename
);
1039 priv
->local_filename
= g_strdup(filename
);
1042 g_object_notify_by_pspec(G_OBJECT(xfer
), properties
[PROP_LOCAL_FILENAME
]);
1046 purple_xfer_set_size(PurpleXfer
*xfer
, goffset size
)
1048 PurpleXferPrivate
*priv
= NULL
;
1050 g_return_if_fail(PURPLE_IS_XFER(xfer
));
1052 priv
= purple_xfer_get_instance_private(xfer
);
1055 g_object_notify_by_pspec(G_OBJECT(xfer
), properties
[PROP_FILE_SIZE
]);
1059 purple_xfer_set_local_port(PurpleXfer
*xfer
, guint16 local_port
)
1061 PurpleXferPrivate
*priv
= NULL
;
1063 g_return_if_fail(PURPLE_IS_XFER(xfer
));
1065 priv
= purple_xfer_get_instance_private(xfer
);
1066 priv
->local_port
= local_port
;
1068 g_object_notify_by_pspec(G_OBJECT(xfer
), properties
[PROP_LOCAL_PORT
]);
1072 purple_xfer_set_bytes_sent(PurpleXfer
*xfer
, goffset bytes_sent
)
1074 PurpleXferPrivate
*priv
= NULL
;
1076 g_return_if_fail(PURPLE_IS_XFER(xfer
));
1078 priv
= purple_xfer_get_instance_private(xfer
);
1079 priv
->bytes_sent
= bytes_sent
;
1081 g_object_notify_by_pspec(G_OBJECT(xfer
), properties
[PROP_BYTES_SENT
]);
1085 purple_xfer_get_ui_ops(PurpleXfer
*xfer
)
1087 PurpleXferPrivate
*priv
= NULL
;
1089 g_return_val_if_fail(PURPLE_IS_XFER(xfer
), NULL
);
1091 priv
= purple_xfer_get_instance_private(xfer
);
1092 return priv
->ui_ops
;
1096 purple_xfer_increase_buffer_size(PurpleXfer
*xfer
)
1098 PurpleXferPrivate
*priv
= purple_xfer_get_instance_private(xfer
);
1100 priv
->current_buffer_size
= MIN(priv
->current_buffer_size
* 1.5,
1101 FT_MAX_BUFFER_SIZE
);
1105 do_read(PurpleXfer
*xfer
, guchar
**buffer
, gsize size
)
1107 PurpleXferPrivate
*priv
= NULL
;
1110 g_return_val_if_fail(PURPLE_IS_XFER(xfer
), 0);
1111 g_return_val_if_fail(buffer
!= NULL
, 0);
1113 priv
= purple_xfer_get_instance_private(xfer
);
1115 *buffer
= g_malloc0(size
);
1117 r
= read(priv
->fd
, *buffer
, size
);
1118 if (r
< 0 && errno
== EAGAIN
) {
1122 } else if (r
== 0) {
1130 purple_xfer_read(PurpleXfer
*xfer
, guchar
**buffer
)
1132 PurpleXferPrivate
*priv
= NULL
;
1133 PurpleXferClass
*klass
= NULL
;
1137 g_return_val_if_fail(PURPLE_IS_XFER(xfer
), 0);
1138 g_return_val_if_fail(buffer
!= NULL
, 0);
1140 priv
= purple_xfer_get_instance_private(xfer
);
1142 if (purple_xfer_get_size(xfer
) == 0) {
1143 s
= priv
->current_buffer_size
;
1146 (gssize
)purple_xfer_get_bytes_remaining(xfer
),
1147 (gssize
)priv
->current_buffer_size
1151 klass
= PURPLE_XFER_GET_CLASS(xfer
);
1152 if(klass
&& klass
->read
) {
1153 r
= klass
->read(xfer
, buffer
, s
);
1155 r
= do_read(xfer
, buffer
, s
);
1158 if (r
>= 0 && (gsize
)r
== priv
->current_buffer_size
) {
1160 * We managed to read the entire buffer. This means our
1161 * network is fast and our buffer is too small, so make it
1164 purple_xfer_increase_buffer_size(xfer
);
1171 do_write(PurpleXfer
*xfer
, const guchar
*buffer
, gsize size
)
1173 PurpleXferPrivate
*priv
= NULL
;
1176 g_return_val_if_fail(PURPLE_IS_XFER(xfer
), 0);
1177 g_return_val_if_fail(buffer
!= NULL
, 0);
1178 g_return_val_if_fail(size
!= 0, 0);
1180 priv
= purple_xfer_get_instance_private(xfer
);
1182 r
= write(priv
->fd
, buffer
, size
);
1183 if (r
< 0 && errno
== EAGAIN
)
1190 purple_xfer_write(PurpleXfer
*xfer
, const guchar
*buffer
, gsize size
)
1192 PurpleXferClass
*klass
= NULL
;
1195 g_return_val_if_fail(PURPLE_IS_XFER(xfer
), 0);
1198 (gssize
)purple_xfer_get_bytes_remaining(xfer
),
1202 klass
= PURPLE_XFER_GET_CLASS(xfer
);
1203 if(klass
&& klass
->write
) {
1204 return klass
->write(xfer
, buffer
, s
);
1207 return do_write(xfer
, buffer
, s
);
1211 purple_xfer_write_file(PurpleXfer
*xfer
, const guchar
*buffer
, gsize size
)
1213 PurpleXferPrivate
*priv
= NULL
;
1214 PurpleXferUiOps
*ui_ops
;
1219 g_return_val_if_fail(PURPLE_IS_XFER(xfer
), FALSE
);
1220 g_return_val_if_fail(buffer
!= NULL
, FALSE
);
1222 priv
= purple_xfer_get_instance_private(xfer
);
1224 ui_ops
= purple_xfer_get_ui_ops(xfer
);
1225 fs_known
= (priv
->size
> 0);
1227 remaining
= purple_xfer_get_bytes_remaining(xfer
);
1228 if (fs_known
&& (goffset
)size
> remaining
) {
1229 purple_debug_warning("xfer",
1230 "Got too much data (truncating at %" G_GOFFSET_FORMAT
1231 ").\n", purple_xfer_get_size(xfer
));
1235 if (ui_ops
&& ui_ops
->ui_write
) {
1236 wc
= ui_ops
->ui_write(xfer
, buffer
, size
);
1238 if (priv
->dest_fp
== NULL
) {
1239 purple_debug_error("xfer",
1240 "File is not opened for writing\n");
1241 purple_xfer_cancel_local(xfer
);
1244 wc
= fwrite(buffer
, 1, size
, priv
->dest_fp
);
1248 purple_debug_error("xfer",
1249 "Unable to write whole buffer.\n");
1250 purple_xfer_cancel_local(xfer
);
1254 purple_xfer_set_bytes_sent(
1256 purple_xfer_get_bytes_sent(xfer
) + size
1263 purple_xfer_read_file(PurpleXfer
*xfer
, guchar
*buffer
, gsize size
)
1265 PurpleXferPrivate
*priv
= NULL
;
1266 PurpleXferUiOps
*ui_ops
;
1269 g_return_val_if_fail(PURPLE_IS_XFER(xfer
), 0);
1270 g_return_val_if_fail(buffer
!= NULL
, 0);
1272 priv
= purple_xfer_get_instance_private(xfer
);
1273 ui_ops
= purple_xfer_get_ui_ops(xfer
);
1275 if (ui_ops
&& ui_ops
->ui_read
) {
1276 guchar
*buffer_got
= NULL
;
1278 got_len
= ui_ops
->ui_read(xfer
, &buffer_got
, size
);
1280 if (got_len
>= 0 && (gsize
)got_len
> size
) {
1282 purple_debug_error("xfer",
1283 "Got too much data from UI.\n");
1284 purple_xfer_cancel_local(xfer
);
1289 memcpy(buffer
, buffer_got
, got_len
);
1292 if (priv
->dest_fp
== NULL
) {
1293 purple_debug_error("xfer",
1294 "File is not opened for reading\n");
1295 purple_xfer_cancel_local(xfer
);
1298 got_len
= fread(buffer
, 1, size
, priv
->dest_fp
);
1299 if ((got_len
< 0 || (gsize
)got_len
!= size
) &&
1300 ferror(priv
->dest_fp
))
1302 purple_debug_error("xfer",
1303 "Unable to read file.\n");
1304 purple_xfer_cancel_local(xfer
);
1310 purple_xfer_set_bytes_sent(xfer
,
1311 purple_xfer_get_bytes_sent(xfer
) + got_len
);
1318 do_transfer(PurpleXfer
*xfer
)
1320 PurpleXferPrivate
*priv
= purple_xfer_get_instance_private(xfer
);
1321 PurpleXferUiOps
*ui_ops
;
1322 guchar
*buffer
= NULL
;
1325 ui_ops
= purple_xfer_get_ui_ops(xfer
);
1327 if (priv
->type
== PURPLE_XFER_TYPE_RECEIVE
) {
1328 r
= purple_xfer_read(xfer
, &buffer
);
1330 if (!purple_xfer_write_file(xfer
, buffer
, r
)) {
1336 purple_xfer_cancel_remote(xfer
);
1340 } else if (priv
->type
== PURPLE_XFER_TYPE_SEND
) {
1343 (gsize
)purple_xfer_get_bytes_remaining(xfer
),
1344 (gsize
)priv
->current_buffer_size
1346 gboolean read
= TRUE
;
1348 /* this is so the protocol can keep the connection open
1349 if it needs to for some odd reason. */
1351 if (priv
->watcher
) {
1352 purple_input_remove(priv
->watcher
);
1353 purple_xfer_set_watcher(xfer
, 0);
1359 if (priv
->buffer
->len
< s
) {
1360 s
-= priv
->buffer
->len
;
1368 buffer
= g_new(guchar
, s
);
1369 result
= purple_xfer_read_file(xfer
, buffer
, s
);
1372 * The UI claimed it was ready, but didn't have any data for
1373 * us... It will call purple_xfer_ui_ready when ready, which
1374 * sets back up this watcher.
1376 if (priv
->watcher
!= 0) {
1377 purple_input_remove(priv
->watcher
);
1378 purple_xfer_set_watcher(xfer
, 0);
1381 /* Need to indicate the protocol is still ready... */
1382 priv
->ready
|= PURPLE_XFER_READY_PROTOCOL
;
1385 g_return_if_reached();
1394 g_byte_array_append(priv
->buffer
, buffer
, result
);
1396 buffer
= priv
->buffer
->data
;
1397 result
= priv
->buffer
->len
;
1400 r
= do_write(xfer
, buffer
, result
);
1403 purple_debug_error("xfer", "do_write failed! %s\n", g_strerror(errno
));
1404 purple_xfer_cancel_remote(xfer
);
1406 /* We don't free buffer if priv->buffer is set, because in
1407 that case buffer doesn't belong to us. */
1410 } else if (r
== result
) {
1412 * We managed to write the entire buffer. This means our
1413 * network is fast and our buffer is too small, so make it
1416 purple_xfer_increase_buffer_size(xfer
);
1418 if (ui_ops
&& ui_ops
->data_not_sent
)
1419 ui_ops
->data_not_sent(xfer
, buffer
+ r
, result
- r
);
1424 * Remove what we wrote
1425 * If we wrote the whole buffer the byte array will be empty
1426 * Otherwise we'll keep what wasn't sent for next time.
1429 g_byte_array_remove_range(priv
->buffer
, 0, r
);
1434 PurpleXferClass
*klass
= PURPLE_XFER_GET_CLASS(xfer
);
1436 if (klass
&& klass
->ack
)
1437 klass
->ack(xfer
, buffer
, r
);
1439 if (ui_ops
!= NULL
&& ui_ops
->update_progress
!= NULL
)
1440 ui_ops
->update_progress(xfer
,
1441 purple_xfer_get_progress(xfer
));
1446 if (purple_xfer_get_bytes_sent(xfer
) >= purple_xfer_get_size(xfer
) &&
1447 !purple_xfer_is_completed(xfer
)) {
1448 purple_xfer_set_completed(xfer
, TRUE
);
1451 /* TODO: Check if above is the only place xfers are marked completed.
1452 * If so, merge these conditions.
1454 if (purple_xfer_is_completed(xfer
)) {
1455 purple_xfer_end(xfer
);
1460 transfer_cb(gpointer data
, gint source
, PurpleInputCondition condition
)
1462 PurpleXfer
*xfer
= data
;
1463 PurpleXferPrivate
*priv
= purple_xfer_get_instance_private(xfer
);
1465 if (priv
->dest_fp
== NULL
) {
1466 /* The UI is moderating its side manually */
1467 if (0 == (priv
->ready
& PURPLE_XFER_READY_UI
)) {
1468 priv
->ready
|= PURPLE_XFER_READY_PROTOCOL
;
1470 purple_input_remove(priv
->watcher
);
1471 purple_xfer_set_watcher(xfer
, 0);
1473 purple_debug_misc("xfer", "Protocol is ready on ft %p, waiting for UI\n", xfer
);
1477 priv
->ready
= PURPLE_XFER_READY_NONE
;
1484 begin_transfer(PurpleXfer
*xfer
, PurpleInputCondition cond
)
1486 PurpleXferClass
*klass
= PURPLE_XFER_GET_CLASS(xfer
);
1487 PurpleXferPrivate
*priv
= purple_xfer_get_instance_private(xfer
);
1488 PurpleXferUiOps
*ui_ops
= purple_xfer_get_ui_ops(xfer
);
1490 if (priv
->start_time
!= 0) {
1491 purple_debug_error("xfer", "Transfer is being started multiple times\n");
1492 g_return_if_reached();
1495 if (ui_ops
== NULL
|| (ui_ops
->ui_read
== NULL
&& ui_ops
->ui_write
== NULL
)) {
1496 priv
->dest_fp
= g_fopen(purple_xfer_get_local_filename(xfer
),
1497 priv
->type
== PURPLE_XFER_TYPE_RECEIVE
? "wb" : "rb");
1499 if (priv
->dest_fp
== NULL
) {
1500 purple_xfer_show_file_error(xfer
, purple_xfer_get_local_filename(xfer
));
1501 purple_xfer_cancel_local(xfer
);
1505 if (fseek(priv
->dest_fp
, priv
->bytes_sent
, SEEK_SET
) != 0) {
1506 purple_debug_error("xfer", "couldn't seek\n");
1507 purple_xfer_show_file_error(xfer
, purple_xfer_get_local_filename(xfer
));
1508 purple_xfer_cancel_local(xfer
);
1513 if (priv
->fd
!= -1) {
1514 purple_xfer_set_watcher(
1516 purple_input_add(priv
->fd
, cond
, transfer_cb
, xfer
)
1520 priv
->start_time
= g_get_monotonic_time();
1522 g_object_notify_by_pspec(G_OBJECT(xfer
), properties
[PROP_START_TIME
]);
1524 if (klass
&& klass
->start
) {
1530 connect_cb(gpointer data
, gint source
, const gchar
*error_message
)
1532 PurpleXfer
*xfer
= PURPLE_XFER(data
);
1535 purple_xfer_cancel_local(xfer
);
1539 purple_xfer_set_fd(xfer
, source
);
1540 begin_transfer(xfer
, PURPLE_INPUT_READ
);
1544 purple_xfer_ui_ready(PurpleXfer
*xfer
)
1546 PurpleXferPrivate
*priv
= NULL
;
1547 PurpleInputCondition cond
= 0;
1549 g_return_if_fail(PURPLE_IS_XFER(xfer
));
1551 priv
= purple_xfer_get_instance_private(xfer
);
1553 priv
->ready
|= PURPLE_XFER_READY_UI
;
1555 if (0 == (priv
->ready
& PURPLE_XFER_READY_PROTOCOL
)) {
1556 purple_debug_misc("xfer", "UI is ready on ft %p, waiting for protocol\n", xfer
);
1560 purple_debug_misc("xfer", "UI (and protocol) ready on ft %p, so proceeding\n", xfer
);
1562 if (priv
->type
== PURPLE_XFER_TYPE_SEND
) {
1563 cond
= PURPLE_INPUT_WRITE
;
1564 } else if (priv
->type
== PURPLE_XFER_TYPE_RECEIVE
) {
1565 cond
= PURPLE_INPUT_READ
;
1568 if (priv
->watcher
== 0 && priv
->fd
!= -1) {
1569 purple_xfer_set_watcher(
1571 purple_input_add(priv
->fd
, cond
, transfer_cb
, xfer
)
1575 priv
->ready
= PURPLE_XFER_READY_NONE
;
1581 purple_xfer_protocol_ready(PurpleXfer
*xfer
)
1583 PurpleXferPrivate
*priv
= NULL
;
1585 g_return_if_fail(PURPLE_IS_XFER(xfer
));
1587 priv
= purple_xfer_get_instance_private(xfer
);
1589 priv
->ready
|= PURPLE_XFER_READY_PROTOCOL
;
1591 /* I don't think fwrite/fread are ever *not* ready */
1592 if (priv
->dest_fp
== NULL
&& 0 == (priv
->ready
& PURPLE_XFER_READY_UI
)) {
1593 purple_debug_misc("xfer", "Protocol is ready on ft %p, waiting for UI\n", xfer
);
1597 purple_debug_misc("xfer", "Protocol (and UI) ready on ft %p, so proceeding\n", xfer
);
1599 priv
->ready
= PURPLE_XFER_READY_NONE
;
1605 purple_xfer_start(PurpleXfer
*xfer
, int fd
, const char *ip
, guint16 port
)
1607 PurpleXferPrivate
*priv
= NULL
;
1608 PurpleInputCondition cond
;
1611 g_return_if_fail(PURPLE_IS_XFER(xfer
));
1613 priv
= purple_xfer_get_instance_private(xfer
);
1615 purple_xfer_set_status(xfer
, PURPLE_XFER_STATUS_STARTED
);
1617 if (priv
->type
== PURPLE_XFER_TYPE_RECEIVE
) {
1618 cond
= PURPLE_INPUT_READ
;
1621 priv
->remote_ip
= g_strdup(ip
);
1622 priv
->remote_port
= port
;
1624 obj
= G_OBJECT(xfer
);
1625 g_object_freeze_notify(obj
);
1626 g_object_notify_by_pspec(obj
, properties
[PROP_REMOTE_IP
]);
1627 g_object_notify_by_pspec(obj
, properties
[PROP_REMOTE_PORT
]);
1628 g_object_thaw_notify(obj
);
1630 /* Establish a file descriptor. */
1631 purple_proxy_connect(
1642 purple_xfer_set_fd(xfer
, fd
);
1645 cond
= PURPLE_INPUT_WRITE
;
1647 purple_xfer_set_fd(xfer
, fd
);
1650 begin_transfer(xfer
, cond
);
1654 purple_xfer_end(PurpleXfer
*xfer
)
1656 PurpleXferClass
*klass
= NULL
;
1657 PurpleXferPrivate
*priv
= NULL
;
1659 g_return_if_fail(PURPLE_IS_XFER(xfer
));
1661 klass
= PURPLE_XFER_GET_CLASS(xfer
);
1662 priv
= purple_xfer_get_instance_private(xfer
);
1664 /* See if we are actually trying to cancel this. */
1665 if (!purple_xfer_is_completed(xfer
)) {
1666 purple_xfer_cancel_local(xfer
);
1670 priv
->end_time
= g_get_monotonic_time();
1672 g_object_notify_by_pspec(G_OBJECT(xfer
), properties
[PROP_END_TIME
]);
1674 if (klass
&& klass
->end
!= NULL
) {
1678 if (priv
->watcher
!= 0) {
1679 purple_input_remove(priv
->watcher
);
1680 purple_xfer_set_watcher(xfer
, 0);
1683 if (priv
->fd
!= -1) {
1684 if (close(priv
->fd
)) {
1685 purple_debug_error("xfer", "closing file descr in purple_xfer_end() failed: %s",
1690 if (priv
->dest_fp
!= NULL
) {
1691 if (fclose(priv
->dest_fp
)) {
1692 purple_debug_error("xfer", "closing dest file in purple_xfer_end() failed: %s",
1695 priv
->dest_fp
= NULL
;
1698 g_object_unref(xfer
);
1702 purple_xfer_add(PurpleXfer
*xfer
)
1704 PurpleXferUiOps
*ui_ops
;
1706 g_return_if_fail(PURPLE_IS_XFER(xfer
));
1708 ui_ops
= purple_xfer_get_ui_ops(xfer
);
1710 if (ui_ops
!= NULL
&& ui_ops
->add_xfer
!= NULL
)
1711 ui_ops
->add_xfer(xfer
);
1715 purple_xfer_cancel_local(PurpleXfer
*xfer
)
1717 PurpleXferClass
*klass
= NULL
;
1718 PurpleXferPrivate
*priv
= NULL
;
1719 PurpleXferUiOps
*ui_ops
;
1722 g_return_if_fail(PURPLE_IS_XFER(xfer
));
1724 klass
= PURPLE_XFER_GET_CLASS(xfer
);
1725 priv
= purple_xfer_get_instance_private(xfer
);
1727 /* TODO: We definitely want to close any open request dialogs associated
1728 with this transfer. However, in some cases the request dialog might
1729 own a reference on the xfer. This happens at least with the "%s wants
1730 to send you %s" dialog from purple_xfer_ask_recv(). In these cases
1731 the ref count will not be decremented when the request dialog is
1732 closed, so the ref count will never reach 0 and the xfer will never
1733 be freed. This is a memleak and should be fixed. It's not clear what
1734 the correct fix is. Probably requests should have a destroy function
1735 that is called when the request is destroyed. But also, ref counting
1736 xfer objects makes this code REALLY complicated. An alternate fix is
1737 to not ref count and instead just make sure the object still exists
1738 when we try to use it. */
1739 purple_request_close_with_handle(xfer
);
1741 purple_xfer_set_status(xfer
, PURPLE_XFER_STATUS_CANCEL_LOCAL
);
1742 priv
->end_time
= g_get_monotonic_time();
1744 g_object_notify_by_pspec(G_OBJECT(xfer
), properties
[PROP_END_TIME
]);
1746 if (purple_xfer_get_filename(xfer
) != NULL
)
1748 msg
= g_strdup_printf(_("You cancelled the transfer of %s"),
1749 purple_xfer_get_filename(xfer
));
1753 msg
= g_strdup(_("File transfer cancelled"));
1755 purple_xfer_conversation_write(xfer
, msg
, FALSE
);
1758 if (priv
->type
== PURPLE_XFER_TYPE_SEND
)
1760 if (klass
&& klass
->cancel_send
) {
1761 klass
->cancel_send(xfer
);
1764 if (klass
&& klass
->cancel_recv
) {
1765 klass
->cancel_recv(xfer
);
1769 if (priv
->watcher
!= 0) {
1770 purple_input_remove(priv
->watcher
);
1771 purple_xfer_set_watcher(xfer
, 0);
1774 if (priv
->fd
!= -1) {
1778 if (priv
->dest_fp
!= NULL
) {
1779 fclose(priv
->dest_fp
);
1780 priv
->dest_fp
= NULL
;
1783 ui_ops
= purple_xfer_get_ui_ops(xfer
);
1785 if (ui_ops
!= NULL
&& ui_ops
->cancel_local
!= NULL
) {
1786 ui_ops
->cancel_local(xfer
);
1789 g_object_unref(xfer
);
1793 purple_xfer_cancel_remote(PurpleXfer
*xfer
)
1795 PurpleXferClass
*klass
= NULL
;
1796 PurpleXferPrivate
*priv
= NULL
;
1797 PurpleXferUiOps
*ui_ops
;
1799 PurpleAccount
*account
;
1802 g_return_if_fail(PURPLE_IS_XFER(xfer
));
1804 klass
= PURPLE_XFER_GET_CLASS(xfer
);
1805 priv
= purple_xfer_get_instance_private(xfer
);
1807 purple_request_close_with_handle(xfer
);
1808 purple_xfer_set_status(xfer
, PURPLE_XFER_STATUS_CANCEL_REMOTE
);
1809 priv
->end_time
= g_get_monotonic_time();
1811 g_object_notify_by_pspec(G_OBJECT(xfer
), properties
[PROP_END_TIME
]);
1813 account
= purple_xfer_get_account(xfer
);
1814 buddy
= purple_blist_find_buddy(account
, priv
->who
);
1816 if (purple_xfer_get_filename(xfer
) != NULL
)
1818 msg
= g_strdup_printf(_("%s cancelled the transfer of %s"),
1819 buddy
? purple_buddy_get_alias(buddy
) : priv
->who
, purple_xfer_get_filename(xfer
));
1823 msg
= g_strdup_printf(_("%s cancelled the file transfer"),
1824 buddy
? purple_buddy_get_alias(buddy
) : priv
->who
);
1826 purple_xfer_conversation_write(xfer
, msg
, TRUE
);
1827 purple_xfer_error(purple_xfer_get_xfer_type(xfer
), account
, priv
->who
, msg
);
1830 if (priv
->type
== PURPLE_XFER_TYPE_SEND
) {
1831 if (klass
&& klass
->cancel_send
) {
1832 klass
->cancel_send(xfer
);
1834 } else if(priv
->type
== PURPLE_XFER_TYPE_RECEIVE
) {
1835 if (klass
&& klass
->cancel_recv
) {
1836 klass
->cancel_recv(xfer
);
1840 if (priv
->watcher
!= 0) {
1841 purple_input_remove(priv
->watcher
);
1842 purple_xfer_set_watcher(xfer
, 0);
1848 if (priv
->dest_fp
!= NULL
) {
1849 fclose(priv
->dest_fp
);
1850 priv
->dest_fp
= NULL
;
1853 ui_ops
= purple_xfer_get_ui_ops(xfer
);
1855 if (ui_ops
!= NULL
&& ui_ops
->cancel_remote
!= NULL
)
1856 ui_ops
->cancel_remote(xfer
);
1858 g_object_unref(xfer
);
1862 purple_xfer_error(PurpleXferType type
, PurpleAccount
*account
, const gchar
*who
, const gchar
*msg
)
1864 gchar
*title
= NULL
;
1866 g_return_if_fail(msg
!= NULL
);
1870 buddy
= purple_blist_find_buddy(account
, who
);
1872 who
= purple_buddy_get_alias(buddy
);
1875 if (type
== PURPLE_XFER_TYPE_SEND
) {
1876 title
= g_strdup_printf(_("File transfer to %s failed."), who
);
1877 } else if (type
== PURPLE_XFER_TYPE_RECEIVE
) {
1878 title
= g_strdup_printf(_("File transfer from %s failed."), who
);
1881 purple_notify_error(NULL
, NULL
, title
, msg
,
1882 purple_request_cpar_from_account(account
));
1888 purple_xfer_update_progress(PurpleXfer
*xfer
)
1890 PurpleXferUiOps
*ui_ops
;
1892 g_return_if_fail(PURPLE_IS_XFER(xfer
));
1894 ui_ops
= purple_xfer_get_ui_ops(xfer
);
1895 if (ui_ops
!= NULL
&& ui_ops
->update_progress
!= NULL
) {
1896 ui_ops
->update_progress(xfer
, purple_xfer_get_progress(xfer
));
1901 purple_xfer_get_thumbnail(PurpleXfer
*xfer
, gsize
*len
)
1903 PurpleXferPrivate
*priv
= NULL
;
1905 g_return_val_if_fail(PURPLE_IS_XFER(xfer
), NULL
);
1907 priv
= purple_xfer_get_instance_private(xfer
);
1910 *len
= priv
->thumbnail_size
;
1913 return priv
->thumbnail_data
;
1917 purple_xfer_get_thumbnail_mimetype(PurpleXfer
*xfer
)
1919 PurpleXferPrivate
*priv
= NULL
;
1921 g_return_val_if_fail(PURPLE_IS_XFER(xfer
), NULL
);
1923 priv
= purple_xfer_get_instance_private(xfer
);
1925 return priv
->thumbnail_mimetype
;
1929 purple_xfer_set_thumbnail(PurpleXfer
*xfer
, gconstpointer thumbnail
,
1930 gsize size
, const gchar
*mimetype
)
1932 PurpleXferPrivate
*priv
= NULL
;
1933 gpointer old_thumbnail_data
;
1934 gchar
*old_mimetype
;
1936 g_return_if_fail(PURPLE_IS_XFER(xfer
));
1938 priv
= purple_xfer_get_instance_private(xfer
);
1940 /* Hold onto these in case they are equal to passed-in pointers */
1941 old_thumbnail_data
= priv
->thumbnail_data
;
1942 old_mimetype
= priv
->thumbnail_mimetype
;
1944 if (thumbnail
&& size
> 0) {
1945 priv
->thumbnail_data
= g_memdup(thumbnail
, size
);
1946 priv
->thumbnail_size
= size
;
1947 priv
->thumbnail_mimetype
= g_strdup(mimetype
);
1949 priv
->thumbnail_data
= NULL
;
1950 priv
->thumbnail_size
= 0;
1951 priv
->thumbnail_mimetype
= NULL
;
1954 /* Now it's safe to free the pointers */
1955 g_free(old_thumbnail_data
);
1956 g_free(old_mimetype
);
1960 purple_xfer_prepare_thumbnail(PurpleXfer
*xfer
, const gchar
*formats
)
1962 PurpleXferUiOps
*ui_ops
;
1964 g_return_if_fail(PURPLE_IS_XFER(xfer
));
1966 ui_ops
= purple_xfer_get_ui_ops(xfer
);
1967 if (ui_ops
&& ui_ops
->add_thumbnail
) {
1968 ui_ops
->add_thumbnail(xfer
, formats
);
1972 void purple_xfer_set_ui_data(PurpleXfer
*xfer
, gpointer ui_data
)
1974 PurpleXferPrivate
*priv
= NULL
;
1976 g_return_if_fail(PURPLE_IS_XFER(xfer
));
1978 priv
= purple_xfer_get_instance_private(xfer
);
1980 priv
->ui_data
= ui_data
;
1982 g_object_notify_by_pspec(G_OBJECT(xfer
), properties
[PROP_UI_DATA
]);
1985 gpointer
purple_xfer_get_ui_data(PurpleXfer
*xfer
)
1987 PurpleXferPrivate
*priv
= NULL
;
1989 g_return_val_if_fail(PURPLE_IS_XFER(xfer
), NULL
);
1991 priv
= purple_xfer_get_instance_private(xfer
);
1993 return priv
->ui_data
;
1996 /**************************************************************************
1998 **************************************************************************/
2000 purple_xfer_set_property(GObject
*obj
, guint param_id
, const GValue
*value
,
2003 PurpleXfer
*xfer
= PURPLE_XFER(obj
);
2004 PurpleXferPrivate
*priv
= purple_xfer_get_instance_private(xfer
);
2008 priv
->type
= g_value_get_enum(value
);
2011 priv
->account
= g_value_get_object(value
);
2013 case PROP_REMOTE_USER
:
2014 purple_xfer_set_remote_user(xfer
, g_value_get_string(value
));
2017 purple_xfer_set_message(xfer
, g_value_get_string(value
));
2020 purple_xfer_set_filename(xfer
, g_value_get_string(value
));
2022 case PROP_LOCAL_FILENAME
:
2023 purple_xfer_set_local_filename(xfer
, g_value_get_string(value
));
2025 case PROP_FILE_SIZE
:
2026 purple_xfer_set_size(xfer
, g_value_get_int64(value
));
2028 case PROP_LOCAL_PORT
:
2029 purple_xfer_set_local_port(xfer
, g_value_get_int(value
));
2032 purple_xfer_set_fd(xfer
, g_value_get_int(value
));
2035 purple_xfer_set_watcher(xfer
, g_value_get_int(value
));
2037 case PROP_BYTES_SENT
:
2038 purple_xfer_set_bytes_sent(xfer
, g_value_get_int64(value
));
2041 purple_xfer_set_status(xfer
, g_value_get_enum(value
));
2044 purple_xfer_set_ui_data(xfer
, g_value_get_pointer(value
));
2047 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj
, param_id
, pspec
);
2053 purple_xfer_get_property(GObject
*obj
, guint param_id
, GValue
*value
,
2056 PurpleXfer
*xfer
= PURPLE_XFER(obj
);
2060 g_value_set_enum(value
, purple_xfer_get_xfer_type(xfer
));
2063 g_value_set_object(value
, purple_xfer_get_account(xfer
));
2065 case PROP_REMOTE_USER
:
2066 g_value_set_string(value
, purple_xfer_get_remote_user(xfer
));
2069 g_value_set_string(value
, purple_xfer_get_message(xfer
));
2072 g_value_set_string(value
, purple_xfer_get_filename(xfer
));
2074 case PROP_LOCAL_FILENAME
:
2075 g_value_set_string(value
, purple_xfer_get_local_filename(xfer
));
2077 case PROP_FILE_SIZE
:
2078 g_value_set_int64(value
, purple_xfer_get_size(xfer
));
2080 case PROP_REMOTE_IP
:
2081 g_value_set_string(value
, purple_xfer_get_remote_ip(xfer
));
2083 case PROP_LOCAL_PORT
:
2084 g_value_set_int(value
, purple_xfer_get_local_port(xfer
));
2086 case PROP_REMOTE_PORT
:
2087 g_value_set_int(value
, purple_xfer_get_remote_port(xfer
));
2090 g_value_set_int(value
, purple_xfer_get_fd(xfer
));
2093 g_value_set_int(value
, purple_xfer_get_watcher(xfer
));
2095 case PROP_BYTES_SENT
:
2096 g_value_set_int64(value
, purple_xfer_get_bytes_sent(xfer
));
2098 case PROP_START_TIME
:
2099 g_value_set_int64(value
, purple_xfer_get_start_time(xfer
));
2102 g_value_set_int64(value
, purple_xfer_get_end_time(xfer
));
2105 g_value_set_enum(value
, purple_xfer_get_status(xfer
));
2108 g_value_set_pointer(value
, purple_xfer_get_ui_data(xfer
));
2111 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj
, param_id
, pspec
);
2117 purple_xfer_init(PurpleXfer
*xfer
)
2119 PurpleXferPrivate
*priv
= purple_xfer_get_instance_private(xfer
);
2121 priv
->ui_ops
= purple_xfers_get_ui_ops();
2122 priv
->current_buffer_size
= FT_INITIAL_BUFFER_SIZE
;
2124 priv
->ready
= PURPLE_XFER_READY_NONE
;
2128 purple_xfer_constructed(GObject
*object
)
2130 PurpleXfer
*xfer
= PURPLE_XFER(object
);
2131 PurpleXferPrivate
*priv
= purple_xfer_get_instance_private(xfer
);
2132 PurpleXferUiOps
*ui_ops
;
2134 G_OBJECT_CLASS(purple_xfer_parent_class
)->constructed(object
);
2136 ui_ops
= purple_xfers_get_ui_ops();
2138 if (ui_ops
&& ui_ops
->data_not_sent
) {
2139 /* If the ui will handle unsent data no need for buffer */
2140 priv
->buffer
= NULL
;
2142 priv
->buffer
= g_byte_array_sized_new(FT_INITIAL_BUFFER_SIZE
);
2145 if (ui_ops
!= NULL
&& ui_ops
->new_xfer
!= NULL
) {
2146 ui_ops
->new_xfer(xfer
);
2149 xfers
= g_list_prepend(xfers
, xfer
);
2153 purple_xfer_finalize(GObject
*object
)
2155 PurpleXfer
*xfer
= PURPLE_XFER(object
);
2156 PurpleXferPrivate
*priv
= purple_xfer_get_instance_private(xfer
);
2157 PurpleXferUiOps
*ui_ops
;
2159 /* Close the file browser, if it's open */
2160 purple_request_close_with_handle(xfer
);
2162 if (purple_xfer_get_status(xfer
) == PURPLE_XFER_STATUS_STARTED
) {
2163 purple_xfer_cancel_local(xfer
);
2166 ui_ops
= purple_xfer_get_ui_ops(xfer
);
2168 if (ui_ops
!= NULL
&& ui_ops
->destroy
!= NULL
) {
2169 ui_ops
->destroy(xfer
);
2172 xfers
= g_list_remove(xfers
, xfer
);
2175 g_free(priv
->filename
);
2176 g_free(priv
->remote_ip
);
2177 g_free(priv
->local_filename
);
2180 g_byte_array_free(priv
->buffer
, TRUE
);
2183 g_free(priv
->thumbnail_data
);
2184 g_free(priv
->thumbnail_mimetype
);
2186 G_OBJECT_CLASS(purple_xfer_parent_class
)->finalize(object
);
2189 /* Class initializer function */
2191 purple_xfer_class_init(PurpleXferClass
*klass
)
2193 GObjectClass
*obj_class
= G_OBJECT_CLASS(klass
);
2195 obj_class
->finalize
= purple_xfer_finalize
;
2196 obj_class
->constructed
= purple_xfer_constructed
;
2197 obj_class
->get_property
= purple_xfer_get_property
;
2198 obj_class
->set_property
= purple_xfer_set_property
;
2200 klass
->write
= do_write
;
2201 klass
->read
= do_read
;
2203 properties
[PROP_TYPE
] = g_param_spec_enum("type", "Transfer type",
2204 "The type of file transfer.", PURPLE_TYPE_XFER_TYPE
,
2205 PURPLE_XFER_TYPE_UNKNOWN
,
2206 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
|
2207 G_PARAM_STATIC_STRINGS
);
2209 properties
[PROP_ACCOUNT
] = g_param_spec_object("account", "Account",
2210 "The account sending or receiving the file.",
2211 PURPLE_TYPE_ACCOUNT
,
2212 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
|
2213 G_PARAM_STATIC_STRINGS
);
2215 properties
[PROP_REMOTE_USER
] = g_param_spec_string("remote-user",
2217 "The name of the remote user.", NULL
,
2218 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT
| G_PARAM_STATIC_STRINGS
);
2220 properties
[PROP_MESSAGE
] = g_param_spec_string("message", "Message",
2221 "The message for the file transfer.", NULL
,
2222 G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS
);
2224 properties
[PROP_FILENAME
] = g_param_spec_string("filename", "Filename",
2225 "The filename for the file transfer.", NULL
,
2226 G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS
);
2228 properties
[PROP_LOCAL_FILENAME
] = g_param_spec_string("local-filename",
2230 "The local filename for the file transfer.", NULL
,
2231 G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS
);
2233 properties
[PROP_FILE_SIZE
] = g_param_spec_int64("file-size", "File size",
2234 "Size of the file in a file transfer.",
2235 G_MININT64
, G_MAXINT64
, 0,
2236 G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS
);
2238 properties
[PROP_REMOTE_IP
] = g_param_spec_string("remote-ip", "Remote IP",
2239 "The remote IP address in the file transfer.", NULL
,
2240 G_PARAM_READABLE
| G_PARAM_STATIC_STRINGS
);
2242 properties
[PROP_LOCAL_PORT
] = g_param_spec_int("local-port", "Local port",
2243 "The local port number in the file transfer.",
2244 G_MININT
, G_MAXINT
, 0,
2245 G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS
);
2247 properties
[PROP_REMOTE_PORT
] = g_param_spec_int("remote-port",
2249 "The remote port number in the file transfer.",
2250 G_MININT
, G_MAXINT
, 0,
2251 G_PARAM_READABLE
| G_PARAM_STATIC_STRINGS
);
2253 properties
[PROP_FD
] = g_param_spec_int("fd", "Socket FD",
2254 "The socket file descriptor.",
2255 G_MININT
, G_MAXINT
, 0,
2256 G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS
);
2258 properties
[PROP_WATCHER
] = g_param_spec_int("watcher", "Watcher",
2259 "The watcher for the file transfer.",
2260 G_MININT
, G_MAXINT
, 0,
2261 G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS
);
2263 properties
[PROP_BYTES_SENT
] = g_param_spec_int64("bytes-sent", "Bytes sent",
2264 "The number of bytes sent (or received) so far.",
2265 G_MININT64
, G_MAXINT64
, 0,
2266 G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS
);
2268 properties
[PROP_START_TIME
] = g_param_spec_int64(
2269 "start-time", "Start time",
2270 "The monotonic time the transfer of a file started.",
2271 G_MININT64
, G_MAXINT64
, 0,
2272 G_PARAM_READABLE
| G_PARAM_STATIC_STRINGS
);
2274 properties
[PROP_END_TIME
] = g_param_spec_int64(
2275 "end-time", "End time",
2276 "The monotonic time the transfer of a file ended.", G_MININT64
,
2277 G_MAXINT64
, 0, G_PARAM_READABLE
| G_PARAM_STATIC_STRINGS
);
2279 properties
[PROP_STATUS
] = g_param_spec_enum("status", "Status",
2280 "The current status for the file transfer.",
2281 PURPLE_TYPE_XFER_STATUS
, PURPLE_XFER_STATUS_UNKNOWN
,
2282 G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS
);
2284 properties
[PROP_UI_DATA
] = g_param_spec_pointer("ui-data", "UI Data",
2285 "The UI specific data for this xfer",
2286 G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS
);
2288 g_object_class_install_properties(obj_class
, PROP_LAST
, properties
);
2292 purple_xfer_new(PurpleAccount
*account
, PurpleXferType type
, const char *who
)
2295 PurpleProtocol
*protocol
;
2297 g_return_val_if_fail(type
!= PURPLE_XFER_TYPE_UNKNOWN
, NULL
);
2298 g_return_val_if_fail(PURPLE_IS_ACCOUNT(account
), NULL
);
2299 g_return_val_if_fail(who
!= NULL
, NULL
);
2301 protocol
= purple_protocols_find(purple_account_get_protocol_id(account
));
2302 if (PURPLE_IS_PROTOCOL_XFER(protocol
)) {
2303 PurpleConnection
*connection
= purple_account_get_connection(account
);
2305 xfer
= purple_protocol_xfer_new_xfer(
2306 PURPLE_PROTOCOL_XFER(protocol
),
2308 /* TODO: this should support the type */
2312 xfer
= g_object_new(PURPLE_TYPE_XFER
,
2323 /**************************************************************************
2324 * File Transfer Subsystem API
2325 **************************************************************************/
2327 purple_xfers_get_all()
2333 purple_xfers_get_handle(void) {
2334 static int handle
= 0;
2340 purple_xfers_init(void) {
2341 void *handle
= purple_xfers_get_handle();
2343 /* register signals */
2344 purple_signal_register(handle
, "file-recv-accept",
2345 purple_marshal_VOID__POINTER
, G_TYPE_NONE
, 1,
2347 purple_signal_register(handle
, "file-send-accept",
2348 purple_marshal_VOID__POINTER
, G_TYPE_NONE
, 1,
2350 purple_signal_register(handle
, "file-recv-start",
2351 purple_marshal_VOID__POINTER
, G_TYPE_NONE
, 1,
2353 purple_signal_register(handle
, "file-send-start",
2354 purple_marshal_VOID__POINTER
, G_TYPE_NONE
, 1,
2356 purple_signal_register(handle
, "file-send-cancel",
2357 purple_marshal_VOID__POINTER
, G_TYPE_NONE
, 1,
2359 purple_signal_register(handle
, "file-recv-cancel",
2360 purple_marshal_VOID__POINTER
, G_TYPE_NONE
, 1,
2362 purple_signal_register(handle
, "file-send-complete",
2363 purple_marshal_VOID__POINTER
, G_TYPE_NONE
, 1,
2365 purple_signal_register(handle
, "file-recv-complete",
2366 purple_marshal_VOID__POINTER
, G_TYPE_NONE
, 1,
2368 purple_signal_register(handle
, "file-recv-request",
2369 purple_marshal_VOID__POINTER
, G_TYPE_NONE
, 1,
2374 purple_xfers_uninit(void)
2376 void *handle
= purple_xfers_get_handle();
2378 purple_signals_disconnect_by_handle(handle
);
2379 purple_signals_unregister_by_instance(handle
);
2383 purple_xfers_set_ui_ops(PurpleXferUiOps
*ops
) {
2388 purple_xfers_get_ui_ops(void) {
2392 /**************************************************************************
2394 **************************************************************************/
2395 static PurpleXferUiOps
*
2396 purple_xfer_ui_ops_copy(PurpleXferUiOps
*ops
)
2398 PurpleXferUiOps
*ops_new
;
2400 g_return_val_if_fail(ops
!= NULL
, NULL
);
2402 ops_new
= g_new(PurpleXferUiOps
, 1);
2409 purple_xfer_ui_ops_get_type(void)
2411 static GType type
= 0;
2414 type
= g_boxed_type_register_static("PurpleXferUiOps",
2415 (GBoxedCopyFunc
)purple_xfer_ui_ops_copy
,
2416 (GBoxedFreeFunc
)g_free
);
2422 /**************************************************************************
2423 * PurpleXferProtocolInterface
2424 **************************************************************************/
2425 G_DEFINE_INTERFACE(PurpleProtocolXfer
, purple_protocol_xfer
, G_TYPE_INVALID
);
2428 purple_protocol_xfer_default_init(PurpleProtocolXferInterface
*face
) {
2432 purple_protocol_xfer_can_receive(PurpleProtocolXfer
*prplxfer
,
2433 PurpleConnection
*connection
,
2436 PurpleProtocolXferInterface
*iface
= NULL
;
2438 g_return_val_if_fail(PURPLE_IS_PROTOCOL_XFER(prplxfer
), FALSE
);
2439 g_return_val_if_fail(PURPLE_IS_CONNECTION(connection
), FALSE
);
2440 g_return_val_if_fail(who
, FALSE
);
2442 iface
= PURPLE_PROTOCOL_XFER_GET_IFACE(prplxfer
);
2443 if(iface
&& iface
->can_receive
)
2444 return iface
->can_receive(prplxfer
, connection
, who
);
2446 /* If the PurpleProtocolXfer doesn't implement this function, we assume
2447 * there are no conditions where we can't send a file to the given user.
2453 purple_protocol_xfer_send_file(PurpleProtocolXfer
*prplxfer
,
2454 PurpleConnection
*connection
,
2456 const gchar
*filename
2458 PurpleProtocolXferInterface
*iface
= NULL
;
2460 g_return_if_fail(PURPLE_IS_PROTOCOL_XFER(prplxfer
));
2461 g_return_if_fail(PURPLE_IS_CONNECTION(connection
));
2462 g_return_if_fail(who
);
2464 iface
= PURPLE_PROTOCOL_XFER_GET_IFACE(prplxfer
);
2465 if(iface
&& iface
->send_file
)
2466 iface
->send_file(prplxfer
, connection
, who
, filename
);
2470 purple_protocol_xfer_new_xfer(PurpleProtocolXfer
*prplxfer
,
2471 PurpleConnection
*connection
,
2474 PurpleProtocolXferInterface
*iface
= NULL
;
2476 g_return_val_if_fail(PURPLE_IS_PROTOCOL_XFER(prplxfer
), FALSE
);
2477 g_return_val_if_fail(PURPLE_IS_CONNECTION(connection
), FALSE
);
2478 g_return_val_if_fail(who
, FALSE
);
2480 iface
= PURPLE_PROTOCOL_XFER_GET_IFACE(prplxfer
);
2481 if(iface
&& iface
->new_xfer
)
2482 return iface
->new_xfer(prplxfer
, connection
, who
);