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
;
1384 g_return_if_reached();
1391 g_byte_array_append(priv
->buffer
, buffer
, result
);
1393 buffer
= priv
->buffer
->data
;
1394 result
= priv
->buffer
->len
;
1397 r
= do_write(xfer
, buffer
, result
);
1400 purple_debug_error("xfer", "do_write failed! %s\n", g_strerror(errno
));
1401 purple_xfer_cancel_remote(xfer
);
1403 /* We don't free buffer if priv->buffer is set, because in
1404 that case buffer doesn't belong to us. */
1407 } else if (r
== result
) {
1409 * We managed to write the entire buffer. This means our
1410 * network is fast and our buffer is too small, so make it
1413 purple_xfer_increase_buffer_size(xfer
);
1415 if (ui_ops
&& ui_ops
->data_not_sent
)
1416 ui_ops
->data_not_sent(xfer
, buffer
+ r
, result
- r
);
1421 * Remove what we wrote
1422 * If we wrote the whole buffer the byte array will be empty
1423 * Otherwise we'll keep what wasn't sent for next time.
1426 g_byte_array_remove_range(priv
->buffer
, 0, r
);
1431 PurpleXferClass
*klass
= PURPLE_XFER_GET_CLASS(xfer
);
1433 if (klass
&& klass
->ack
)
1434 klass
->ack(xfer
, buffer
, r
);
1438 if (ui_ops
!= NULL
&& ui_ops
->update_progress
!= NULL
)
1439 ui_ops
->update_progress(xfer
,
1440 purple_xfer_get_progress(xfer
));
1443 if (purple_xfer_get_bytes_sent(xfer
) >= purple_xfer_get_size(xfer
) &&
1444 !purple_xfer_is_completed(xfer
)) {
1445 purple_xfer_set_completed(xfer
, TRUE
);
1448 /* TODO: Check if above is the only place xfers are marked completed.
1449 * If so, merge these conditions.
1451 if (purple_xfer_is_completed(xfer
)) {
1452 purple_xfer_end(xfer
);
1457 transfer_cb(gpointer data
, gint source
, PurpleInputCondition condition
)
1459 PurpleXfer
*xfer
= data
;
1460 PurpleXferPrivate
*priv
= purple_xfer_get_instance_private(xfer
);
1462 if (priv
->dest_fp
== NULL
) {
1463 /* The UI is moderating its side manually */
1464 if (0 == (priv
->ready
& PURPLE_XFER_READY_UI
)) {
1465 priv
->ready
|= PURPLE_XFER_READY_PROTOCOL
;
1467 purple_input_remove(priv
->watcher
);
1468 purple_xfer_set_watcher(xfer
, 0);
1470 purple_debug_misc("xfer", "Protocol is ready on ft %p, waiting for UI\n", xfer
);
1474 priv
->ready
= PURPLE_XFER_READY_NONE
;
1481 begin_transfer(PurpleXfer
*xfer
, PurpleInputCondition cond
)
1483 PurpleXferClass
*klass
= PURPLE_XFER_GET_CLASS(xfer
);
1484 PurpleXferPrivate
*priv
= purple_xfer_get_instance_private(xfer
);
1485 PurpleXferUiOps
*ui_ops
= purple_xfer_get_ui_ops(xfer
);
1487 if (priv
->start_time
!= 0) {
1488 purple_debug_error("xfer", "Transfer is being started multiple times\n");
1489 g_return_if_reached();
1492 if (ui_ops
== NULL
|| (ui_ops
->ui_read
== NULL
&& ui_ops
->ui_write
== NULL
)) {
1493 priv
->dest_fp
= g_fopen(purple_xfer_get_local_filename(xfer
),
1494 priv
->type
== PURPLE_XFER_TYPE_RECEIVE
? "wb" : "rb");
1496 if (priv
->dest_fp
== NULL
) {
1497 purple_xfer_show_file_error(xfer
, purple_xfer_get_local_filename(xfer
));
1498 purple_xfer_cancel_local(xfer
);
1502 if (fseek(priv
->dest_fp
, priv
->bytes_sent
, SEEK_SET
) != 0) {
1503 purple_debug_error("xfer", "couldn't seek\n");
1504 purple_xfer_show_file_error(xfer
, purple_xfer_get_local_filename(xfer
));
1505 purple_xfer_cancel_local(xfer
);
1510 if (priv
->fd
!= -1) {
1511 purple_xfer_set_watcher(
1513 purple_input_add(priv
->fd
, cond
, transfer_cb
, xfer
)
1517 priv
->start_time
= g_get_monotonic_time();
1519 g_object_notify_by_pspec(G_OBJECT(xfer
), properties
[PROP_START_TIME
]);
1521 if (klass
&& klass
->start
) {
1527 connect_cb(gpointer data
, gint source
, const gchar
*error_message
)
1529 PurpleXfer
*xfer
= PURPLE_XFER(data
);
1532 purple_xfer_cancel_local(xfer
);
1536 purple_xfer_set_fd(xfer
, source
);
1537 begin_transfer(xfer
, PURPLE_INPUT_READ
);
1541 purple_xfer_ui_ready(PurpleXfer
*xfer
)
1543 PurpleXferPrivate
*priv
= NULL
;
1544 PurpleInputCondition cond
;
1546 g_return_if_fail(PURPLE_IS_XFER(xfer
));
1548 priv
= purple_xfer_get_instance_private(xfer
);
1550 priv
->ready
|= PURPLE_XFER_READY_UI
;
1552 if (0 == (priv
->ready
& PURPLE_XFER_READY_PROTOCOL
)) {
1553 purple_debug_misc("xfer", "UI is ready on ft %p, waiting for protocol\n", xfer
);
1557 purple_debug_misc("xfer", "UI (and protocol) ready on ft %p, so proceeding\n", xfer
);
1559 if (priv
->type
== PURPLE_XFER_TYPE_SEND
) {
1560 cond
= PURPLE_INPUT_WRITE
;
1561 } else if (priv
->type
== PURPLE_XFER_TYPE_RECEIVE
) {
1562 cond
= PURPLE_INPUT_READ
;
1565 if (priv
->watcher
== 0 && priv
->fd
!= -1) {
1566 purple_xfer_set_watcher(
1568 purple_input_add(priv
->fd
, cond
, transfer_cb
, xfer
)
1572 priv
->ready
= PURPLE_XFER_READY_NONE
;
1578 purple_xfer_protocol_ready(PurpleXfer
*xfer
)
1580 PurpleXferPrivate
*priv
= NULL
;
1582 g_return_if_fail(PURPLE_IS_XFER(xfer
));
1584 priv
= purple_xfer_get_instance_private(xfer
);
1586 priv
->ready
|= PURPLE_XFER_READY_PROTOCOL
;
1588 /* I don't think fwrite/fread are ever *not* ready */
1589 if (priv
->dest_fp
== NULL
&& 0 == (priv
->ready
& PURPLE_XFER_READY_UI
)) {
1590 purple_debug_misc("xfer", "Protocol is ready on ft %p, waiting for UI\n", xfer
);
1594 purple_debug_misc("xfer", "Protocol (and UI) ready on ft %p, so proceeding\n", xfer
);
1596 priv
->ready
= PURPLE_XFER_READY_NONE
;
1602 purple_xfer_start(PurpleXfer
*xfer
, int fd
, const char *ip
, guint16 port
)
1604 PurpleXferPrivate
*priv
= NULL
;
1605 PurpleInputCondition cond
;
1608 g_return_if_fail(PURPLE_IS_XFER(xfer
));
1610 priv
= purple_xfer_get_instance_private(xfer
);
1612 purple_xfer_set_status(xfer
, PURPLE_XFER_STATUS_STARTED
);
1614 if (priv
->type
== PURPLE_XFER_TYPE_RECEIVE
) {
1615 cond
= PURPLE_INPUT_READ
;
1618 priv
->remote_ip
= g_strdup(ip
);
1619 priv
->remote_port
= port
;
1621 obj
= G_OBJECT(xfer
);
1622 g_object_freeze_notify(obj
);
1623 g_object_notify_by_pspec(obj
, properties
[PROP_REMOTE_IP
]);
1624 g_object_notify_by_pspec(obj
, properties
[PROP_REMOTE_PORT
]);
1625 g_object_thaw_notify(obj
);
1627 /* Establish a file descriptor. */
1628 purple_proxy_connect(
1639 purple_xfer_set_fd(xfer
, fd
);
1642 cond
= PURPLE_INPUT_WRITE
;
1644 purple_xfer_set_fd(xfer
, fd
);
1647 begin_transfer(xfer
, cond
);
1651 purple_xfer_end(PurpleXfer
*xfer
)
1653 PurpleXferClass
*klass
= NULL
;
1654 PurpleXferPrivate
*priv
= NULL
;
1656 g_return_if_fail(PURPLE_IS_XFER(xfer
));
1658 klass
= PURPLE_XFER_GET_CLASS(xfer
);
1659 priv
= purple_xfer_get_instance_private(xfer
);
1661 /* See if we are actually trying to cancel this. */
1662 if (!purple_xfer_is_completed(xfer
)) {
1663 purple_xfer_cancel_local(xfer
);
1667 priv
->end_time
= g_get_monotonic_time();
1669 g_object_notify_by_pspec(G_OBJECT(xfer
), properties
[PROP_END_TIME
]);
1671 if (klass
&& klass
->end
!= NULL
) {
1675 if (priv
->watcher
!= 0) {
1676 purple_input_remove(priv
->watcher
);
1677 purple_xfer_set_watcher(xfer
, 0);
1680 if (priv
->fd
!= -1) {
1681 if (close(priv
->fd
)) {
1682 purple_debug_error("xfer", "closing file descr in purple_xfer_end() failed: %s",
1687 if (priv
->dest_fp
!= NULL
) {
1688 if (fclose(priv
->dest_fp
)) {
1689 purple_debug_error("xfer", "closing dest file in purple_xfer_end() failed: %s",
1692 priv
->dest_fp
= NULL
;
1695 g_object_unref(xfer
);
1699 purple_xfer_add(PurpleXfer
*xfer
)
1701 PurpleXferUiOps
*ui_ops
;
1703 g_return_if_fail(PURPLE_IS_XFER(xfer
));
1705 ui_ops
= purple_xfer_get_ui_ops(xfer
);
1707 if (ui_ops
!= NULL
&& ui_ops
->add_xfer
!= NULL
)
1708 ui_ops
->add_xfer(xfer
);
1712 purple_xfer_cancel_local(PurpleXfer
*xfer
)
1714 PurpleXferClass
*klass
= NULL
;
1715 PurpleXferPrivate
*priv
= NULL
;
1716 PurpleXferUiOps
*ui_ops
;
1719 g_return_if_fail(PURPLE_IS_XFER(xfer
));
1721 klass
= PURPLE_XFER_GET_CLASS(xfer
);
1722 priv
= purple_xfer_get_instance_private(xfer
);
1724 /* TODO: We definitely want to close any open request dialogs associated
1725 with this transfer. However, in some cases the request dialog might
1726 own a reference on the xfer. This happens at least with the "%s wants
1727 to send you %s" dialog from purple_xfer_ask_recv(). In these cases
1728 the ref count will not be decremented when the request dialog is
1729 closed, so the ref count will never reach 0 and the xfer will never
1730 be freed. This is a memleak and should be fixed. It's not clear what
1731 the correct fix is. Probably requests should have a destroy function
1732 that is called when the request is destroyed. But also, ref counting
1733 xfer objects makes this code REALLY complicated. An alternate fix is
1734 to not ref count and instead just make sure the object still exists
1735 when we try to use it. */
1736 purple_request_close_with_handle(xfer
);
1738 purple_xfer_set_status(xfer
, PURPLE_XFER_STATUS_CANCEL_LOCAL
);
1739 priv
->end_time
= g_get_monotonic_time();
1741 g_object_notify_by_pspec(G_OBJECT(xfer
), properties
[PROP_END_TIME
]);
1743 if (purple_xfer_get_filename(xfer
) != NULL
)
1745 msg
= g_strdup_printf(_("You cancelled the transfer of %s"),
1746 purple_xfer_get_filename(xfer
));
1750 msg
= g_strdup(_("File transfer cancelled"));
1752 purple_xfer_conversation_write(xfer
, msg
, FALSE
);
1755 if (priv
->type
== PURPLE_XFER_TYPE_SEND
)
1757 if (klass
&& klass
->cancel_send
) {
1758 klass
->cancel_send(xfer
);
1761 if (klass
&& klass
->cancel_recv
) {
1762 klass
->cancel_recv(xfer
);
1766 if (priv
->watcher
!= 0) {
1767 purple_input_remove(priv
->watcher
);
1768 purple_xfer_set_watcher(xfer
, 0);
1771 if (priv
->fd
!= -1) {
1775 if (priv
->dest_fp
!= NULL
) {
1776 fclose(priv
->dest_fp
);
1777 priv
->dest_fp
= NULL
;
1780 ui_ops
= purple_xfer_get_ui_ops(xfer
);
1782 if (ui_ops
!= NULL
&& ui_ops
->cancel_local
!= NULL
) {
1783 ui_ops
->cancel_local(xfer
);
1786 g_object_unref(xfer
);
1790 purple_xfer_cancel_remote(PurpleXfer
*xfer
)
1792 PurpleXferClass
*klass
= NULL
;
1793 PurpleXferPrivate
*priv
= NULL
;
1794 PurpleXferUiOps
*ui_ops
;
1796 PurpleAccount
*account
;
1799 g_return_if_fail(PURPLE_IS_XFER(xfer
));
1801 klass
= PURPLE_XFER_GET_CLASS(xfer
);
1802 priv
= purple_xfer_get_instance_private(xfer
);
1804 purple_request_close_with_handle(xfer
);
1805 purple_xfer_set_status(xfer
, PURPLE_XFER_STATUS_CANCEL_REMOTE
);
1806 priv
->end_time
= g_get_monotonic_time();
1808 g_object_notify_by_pspec(G_OBJECT(xfer
), properties
[PROP_END_TIME
]);
1810 account
= purple_xfer_get_account(xfer
);
1811 buddy
= purple_blist_find_buddy(account
, priv
->who
);
1813 if (purple_xfer_get_filename(xfer
) != NULL
)
1815 msg
= g_strdup_printf(_("%s cancelled the transfer of %s"),
1816 buddy
? purple_buddy_get_alias(buddy
) : priv
->who
, purple_xfer_get_filename(xfer
));
1820 msg
= g_strdup_printf(_("%s cancelled the file transfer"),
1821 buddy
? purple_buddy_get_alias(buddy
) : priv
->who
);
1823 purple_xfer_conversation_write(xfer
, msg
, TRUE
);
1824 purple_xfer_error(purple_xfer_get_xfer_type(xfer
), account
, priv
->who
, msg
);
1827 if (priv
->type
== PURPLE_XFER_TYPE_SEND
) {
1828 if (klass
&& klass
->cancel_send
) {
1829 klass
->cancel_send(xfer
);
1831 } else if(priv
->type
== PURPLE_XFER_TYPE_RECEIVE
) {
1832 if (klass
&& klass
->cancel_recv
) {
1833 klass
->cancel_recv(xfer
);
1837 if (priv
->watcher
!= 0) {
1838 purple_input_remove(priv
->watcher
);
1839 purple_xfer_set_watcher(xfer
, 0);
1845 if (priv
->dest_fp
!= NULL
) {
1846 fclose(priv
->dest_fp
);
1847 priv
->dest_fp
= NULL
;
1850 ui_ops
= purple_xfer_get_ui_ops(xfer
);
1852 if (ui_ops
!= NULL
&& ui_ops
->cancel_remote
!= NULL
)
1853 ui_ops
->cancel_remote(xfer
);
1855 g_object_unref(xfer
);
1859 purple_xfer_error(PurpleXferType type
, PurpleAccount
*account
, const gchar
*who
, const gchar
*msg
)
1863 g_return_if_fail(msg
!= NULL
);
1867 buddy
= purple_blist_find_buddy(account
, who
);
1869 who
= purple_buddy_get_alias(buddy
);
1872 if (type
== PURPLE_XFER_TYPE_SEND
) {
1873 title
= g_strdup_printf(_("File transfer to %s failed."), who
);
1874 } else if (type
== PURPLE_XFER_TYPE_RECEIVE
) {
1875 title
= g_strdup_printf(_("File transfer from %s failed."), who
);
1878 purple_notify_error(NULL
, NULL
, title
, msg
,
1879 purple_request_cpar_from_account(account
));
1885 purple_xfer_update_progress(PurpleXfer
*xfer
)
1887 PurpleXferUiOps
*ui_ops
;
1889 g_return_if_fail(PURPLE_IS_XFER(xfer
));
1891 ui_ops
= purple_xfer_get_ui_ops(xfer
);
1892 if (ui_ops
!= NULL
&& ui_ops
->update_progress
!= NULL
) {
1893 ui_ops
->update_progress(xfer
, purple_xfer_get_progress(xfer
));
1898 purple_xfer_get_thumbnail(PurpleXfer
*xfer
, gsize
*len
)
1900 PurpleXferPrivate
*priv
= NULL
;
1902 g_return_val_if_fail(PURPLE_IS_XFER(xfer
), NULL
);
1904 priv
= purple_xfer_get_instance_private(xfer
);
1907 *len
= priv
->thumbnail_size
;
1910 return priv
->thumbnail_data
;
1914 purple_xfer_get_thumbnail_mimetype(PurpleXfer
*xfer
)
1916 PurpleXferPrivate
*priv
= NULL
;
1918 g_return_val_if_fail(PURPLE_IS_XFER(xfer
), NULL
);
1920 priv
= purple_xfer_get_instance_private(xfer
);
1922 return priv
->thumbnail_mimetype
;
1926 purple_xfer_set_thumbnail(PurpleXfer
*xfer
, gconstpointer thumbnail
,
1927 gsize size
, const gchar
*mimetype
)
1929 PurpleXferPrivate
*priv
= NULL
;
1930 gpointer old_thumbnail_data
;
1931 gchar
*old_mimetype
;
1933 g_return_if_fail(PURPLE_IS_XFER(xfer
));
1935 priv
= purple_xfer_get_instance_private(xfer
);
1937 /* Hold onto these in case they are equal to passed-in pointers */
1938 old_thumbnail_data
= priv
->thumbnail_data
;
1939 old_mimetype
= priv
->thumbnail_mimetype
;
1941 if (thumbnail
&& size
> 0) {
1942 priv
->thumbnail_data
= g_memdup(thumbnail
, size
);
1943 priv
->thumbnail_size
= size
;
1944 priv
->thumbnail_mimetype
= g_strdup(mimetype
);
1946 priv
->thumbnail_data
= NULL
;
1947 priv
->thumbnail_size
= 0;
1948 priv
->thumbnail_mimetype
= NULL
;
1951 /* Now it's safe to free the pointers */
1952 g_free(old_thumbnail_data
);
1953 g_free(old_mimetype
);
1957 purple_xfer_prepare_thumbnail(PurpleXfer
*xfer
, const gchar
*formats
)
1959 PurpleXferUiOps
*ui_ops
;
1961 g_return_if_fail(PURPLE_IS_XFER(xfer
));
1963 ui_ops
= purple_xfer_get_ui_ops(xfer
);
1964 if (ui_ops
&& ui_ops
->add_thumbnail
) {
1965 ui_ops
->add_thumbnail(xfer
, formats
);
1969 void purple_xfer_set_ui_data(PurpleXfer
*xfer
, gpointer ui_data
)
1971 PurpleXferPrivate
*priv
= NULL
;
1973 g_return_if_fail(PURPLE_IS_XFER(xfer
));
1975 priv
= purple_xfer_get_instance_private(xfer
);
1977 priv
->ui_data
= ui_data
;
1979 g_object_notify_by_pspec(G_OBJECT(xfer
), properties
[PROP_UI_DATA
]);
1982 gpointer
purple_xfer_get_ui_data(PurpleXfer
*xfer
)
1984 PurpleXferPrivate
*priv
= NULL
;
1986 g_return_val_if_fail(PURPLE_IS_XFER(xfer
), NULL
);
1988 priv
= purple_xfer_get_instance_private(xfer
);
1990 return priv
->ui_data
;
1993 /**************************************************************************
1995 **************************************************************************/
1997 purple_xfer_set_property(GObject
*obj
, guint param_id
, const GValue
*value
,
2000 PurpleXfer
*xfer
= PURPLE_XFER(obj
);
2001 PurpleXferPrivate
*priv
= purple_xfer_get_instance_private(xfer
);
2005 priv
->type
= g_value_get_enum(value
);
2008 priv
->account
= g_value_get_object(value
);
2010 case PROP_REMOTE_USER
:
2011 purple_xfer_set_remote_user(xfer
, g_value_get_string(value
));
2014 purple_xfer_set_message(xfer
, g_value_get_string(value
));
2017 purple_xfer_set_filename(xfer
, g_value_get_string(value
));
2019 case PROP_LOCAL_FILENAME
:
2020 purple_xfer_set_local_filename(xfer
, g_value_get_string(value
));
2022 case PROP_FILE_SIZE
:
2023 purple_xfer_set_size(xfer
, g_value_get_int64(value
));
2025 case PROP_LOCAL_PORT
:
2026 purple_xfer_set_local_port(xfer
, g_value_get_int(value
));
2029 purple_xfer_set_fd(xfer
, g_value_get_int(value
));
2032 purple_xfer_set_watcher(xfer
, g_value_get_int(value
));
2034 case PROP_BYTES_SENT
:
2035 purple_xfer_set_bytes_sent(xfer
, g_value_get_int64(value
));
2038 purple_xfer_set_status(xfer
, g_value_get_enum(value
));
2041 purple_xfer_set_ui_data(xfer
, g_value_get_pointer(value
));
2044 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj
, param_id
, pspec
);
2050 purple_xfer_get_property(GObject
*obj
, guint param_id
, GValue
*value
,
2053 PurpleXfer
*xfer
= PURPLE_XFER(obj
);
2057 g_value_set_enum(value
, purple_xfer_get_xfer_type(xfer
));
2060 g_value_set_object(value
, purple_xfer_get_account(xfer
));
2062 case PROP_REMOTE_USER
:
2063 g_value_set_string(value
, purple_xfer_get_remote_user(xfer
));
2066 g_value_set_string(value
, purple_xfer_get_message(xfer
));
2069 g_value_set_string(value
, purple_xfer_get_filename(xfer
));
2071 case PROP_LOCAL_FILENAME
:
2072 g_value_set_string(value
, purple_xfer_get_local_filename(xfer
));
2074 case PROP_FILE_SIZE
:
2075 g_value_set_int64(value
, purple_xfer_get_size(xfer
));
2077 case PROP_REMOTE_IP
:
2078 g_value_set_string(value
, purple_xfer_get_remote_ip(xfer
));
2080 case PROP_LOCAL_PORT
:
2081 g_value_set_int(value
, purple_xfer_get_local_port(xfer
));
2083 case PROP_REMOTE_PORT
:
2084 g_value_set_int(value
, purple_xfer_get_remote_port(xfer
));
2087 g_value_set_int(value
, purple_xfer_get_fd(xfer
));
2090 g_value_set_int(value
, purple_xfer_get_watcher(xfer
));
2092 case PROP_BYTES_SENT
:
2093 g_value_set_int64(value
, purple_xfer_get_bytes_sent(xfer
));
2095 case PROP_START_TIME
:
2096 g_value_set_int64(value
, purple_xfer_get_start_time(xfer
));
2099 g_value_set_int64(value
, purple_xfer_get_end_time(xfer
));
2102 g_value_set_enum(value
, purple_xfer_get_status(xfer
));
2105 g_value_set_pointer(value
, purple_xfer_get_ui_data(xfer
));
2108 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj
, param_id
, pspec
);
2114 purple_xfer_init(PurpleXfer
*xfer
)
2116 PurpleXferPrivate
*priv
= purple_xfer_get_instance_private(xfer
);
2118 priv
->ui_ops
= purple_xfers_get_ui_ops();
2119 priv
->current_buffer_size
= FT_INITIAL_BUFFER_SIZE
;
2121 priv
->ready
= PURPLE_XFER_READY_NONE
;
2125 purple_xfer_constructed(GObject
*object
)
2127 PurpleXfer
*xfer
= PURPLE_XFER(object
);
2128 PurpleXferPrivate
*priv
= purple_xfer_get_instance_private(xfer
);
2129 PurpleXferUiOps
*ui_ops
;
2131 G_OBJECT_CLASS(purple_xfer_parent_class
)->constructed(object
);
2133 ui_ops
= purple_xfers_get_ui_ops();
2135 if (ui_ops
&& ui_ops
->data_not_sent
) {
2136 /* If the ui will handle unsent data no need for buffer */
2137 priv
->buffer
= NULL
;
2139 priv
->buffer
= g_byte_array_sized_new(FT_INITIAL_BUFFER_SIZE
);
2142 if (ui_ops
!= NULL
&& ui_ops
->new_xfer
!= NULL
) {
2143 ui_ops
->new_xfer(xfer
);
2146 xfers
= g_list_prepend(xfers
, xfer
);
2150 purple_xfer_finalize(GObject
*object
)
2152 PurpleXfer
*xfer
= PURPLE_XFER(object
);
2153 PurpleXferPrivate
*priv
= purple_xfer_get_instance_private(xfer
);
2154 PurpleXferUiOps
*ui_ops
;
2156 /* Close the file browser, if it's open */
2157 purple_request_close_with_handle(xfer
);
2159 if (purple_xfer_get_status(xfer
) == PURPLE_XFER_STATUS_STARTED
) {
2160 purple_xfer_cancel_local(xfer
);
2163 ui_ops
= purple_xfer_get_ui_ops(xfer
);
2165 if (ui_ops
!= NULL
&& ui_ops
->destroy
!= NULL
) {
2166 ui_ops
->destroy(xfer
);
2169 xfers
= g_list_remove(xfers
, xfer
);
2172 g_free(priv
->filename
);
2173 g_free(priv
->remote_ip
);
2174 g_free(priv
->local_filename
);
2177 g_byte_array_free(priv
->buffer
, TRUE
);
2180 g_free(priv
->thumbnail_data
);
2181 g_free(priv
->thumbnail_mimetype
);
2183 G_OBJECT_CLASS(purple_xfer_parent_class
)->finalize(object
);
2186 /* Class initializer function */
2188 purple_xfer_class_init(PurpleXferClass
*klass
)
2190 GObjectClass
*obj_class
= G_OBJECT_CLASS(klass
);
2192 obj_class
->finalize
= purple_xfer_finalize
;
2193 obj_class
->constructed
= purple_xfer_constructed
;
2194 obj_class
->get_property
= purple_xfer_get_property
;
2195 obj_class
->set_property
= purple_xfer_set_property
;
2197 klass
->write
= do_write
;
2198 klass
->read
= do_read
;
2200 properties
[PROP_TYPE
] = g_param_spec_enum("type", "Transfer type",
2201 "The type of file transfer.", PURPLE_TYPE_XFER_TYPE
,
2202 PURPLE_XFER_TYPE_UNKNOWN
,
2203 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
|
2204 G_PARAM_STATIC_STRINGS
);
2206 properties
[PROP_ACCOUNT
] = g_param_spec_object("account", "Account",
2207 "The account sending or receiving the file.",
2208 PURPLE_TYPE_ACCOUNT
,
2209 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
|
2210 G_PARAM_STATIC_STRINGS
);
2212 properties
[PROP_REMOTE_USER
] = g_param_spec_string("remote-user",
2214 "The name of the remote user.", NULL
,
2215 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT
| G_PARAM_STATIC_STRINGS
);
2217 properties
[PROP_MESSAGE
] = g_param_spec_string("message", "Message",
2218 "The message for the file transfer.", NULL
,
2219 G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS
);
2221 properties
[PROP_FILENAME
] = g_param_spec_string("filename", "Filename",
2222 "The filename for the file transfer.", NULL
,
2223 G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS
);
2225 properties
[PROP_LOCAL_FILENAME
] = g_param_spec_string("local-filename",
2227 "The local filename for the file transfer.", NULL
,
2228 G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS
);
2230 properties
[PROP_FILE_SIZE
] = g_param_spec_int64("file-size", "File size",
2231 "Size of the file in a file transfer.",
2232 G_MININT64
, G_MAXINT64
, 0,
2233 G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS
);
2235 properties
[PROP_REMOTE_IP
] = g_param_spec_string("remote-ip", "Remote IP",
2236 "The remote IP address in the file transfer.", NULL
,
2237 G_PARAM_READABLE
| G_PARAM_STATIC_STRINGS
);
2239 properties
[PROP_LOCAL_PORT
] = g_param_spec_int("local-port", "Local port",
2240 "The local port number in the file transfer.",
2241 G_MININT
, G_MAXINT
, 0,
2242 G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS
);
2244 properties
[PROP_REMOTE_PORT
] = g_param_spec_int("remote-port",
2246 "The remote port number in the file transfer.",
2247 G_MININT
, G_MAXINT
, 0,
2248 G_PARAM_READABLE
| G_PARAM_STATIC_STRINGS
);
2250 properties
[PROP_FD
] = g_param_spec_int("fd", "Socket FD",
2251 "The socket file descriptor.",
2252 G_MININT
, G_MAXINT
, 0,
2253 G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS
);
2255 properties
[PROP_WATCHER
] = g_param_spec_int("watcher", "Watcher",
2256 "The watcher for the file transfer.",
2257 G_MININT
, G_MAXINT
, 0,
2258 G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS
);
2260 properties
[PROP_BYTES_SENT
] = g_param_spec_int64("bytes-sent", "Bytes sent",
2261 "The number of bytes sent (or received) so far.",
2262 G_MININT64
, G_MAXINT64
, 0,
2263 G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS
);
2265 properties
[PROP_START_TIME
] = g_param_spec_int64(
2266 "start-time", "Start time",
2267 "The monotonic time the transfer of a file started.",
2268 G_MININT64
, G_MAXINT64
, 0,
2269 G_PARAM_READABLE
| G_PARAM_STATIC_STRINGS
);
2271 properties
[PROP_END_TIME
] = g_param_spec_int64(
2272 "end-time", "End time",
2273 "The monotonic time the transfer of a file ended.", G_MININT64
,
2274 G_MAXINT64
, 0, G_PARAM_READABLE
| G_PARAM_STATIC_STRINGS
);
2276 properties
[PROP_STATUS
] = g_param_spec_enum("status", "Status",
2277 "The current status for the file transfer.",
2278 PURPLE_TYPE_XFER_STATUS
, PURPLE_XFER_STATUS_UNKNOWN
,
2279 G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS
);
2281 properties
[PROP_UI_DATA
] = g_param_spec_pointer("ui-data", "UI Data",
2282 "The UI specific data for this xfer",
2283 G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS
);
2285 g_object_class_install_properties(obj_class
, PROP_LAST
, properties
);
2289 purple_xfer_new(PurpleAccount
*account
, PurpleXferType type
, const char *who
)
2292 PurpleProtocol
*protocol
;
2294 g_return_val_if_fail(type
!= PURPLE_XFER_TYPE_UNKNOWN
, NULL
);
2295 g_return_val_if_fail(PURPLE_IS_ACCOUNT(account
), NULL
);
2296 g_return_val_if_fail(who
!= NULL
, NULL
);
2298 protocol
= purple_protocols_find(purple_account_get_protocol_id(account
));
2299 if (PURPLE_IS_PROTOCOL_XFER(protocol
)) {
2300 PurpleConnection
*connection
= purple_account_get_connection(account
);
2302 xfer
= purple_protocol_xfer_new_xfer(
2303 PURPLE_PROTOCOL_XFER(protocol
),
2305 /* TODO: this should support the type */
2309 xfer
= g_object_new(PURPLE_TYPE_XFER
,
2320 /**************************************************************************
2321 * File Transfer Subsystem API
2322 **************************************************************************/
2324 purple_xfers_get_all()
2330 purple_xfers_get_handle(void) {
2331 static int handle
= 0;
2337 purple_xfers_init(void) {
2338 void *handle
= purple_xfers_get_handle();
2340 /* register signals */
2341 purple_signal_register(handle
, "file-recv-accept",
2342 purple_marshal_VOID__POINTER
, G_TYPE_NONE
, 1,
2344 purple_signal_register(handle
, "file-send-accept",
2345 purple_marshal_VOID__POINTER
, G_TYPE_NONE
, 1,
2347 purple_signal_register(handle
, "file-recv-start",
2348 purple_marshal_VOID__POINTER
, G_TYPE_NONE
, 1,
2350 purple_signal_register(handle
, "file-send-start",
2351 purple_marshal_VOID__POINTER
, G_TYPE_NONE
, 1,
2353 purple_signal_register(handle
, "file-send-cancel",
2354 purple_marshal_VOID__POINTER
, G_TYPE_NONE
, 1,
2356 purple_signal_register(handle
, "file-recv-cancel",
2357 purple_marshal_VOID__POINTER
, G_TYPE_NONE
, 1,
2359 purple_signal_register(handle
, "file-send-complete",
2360 purple_marshal_VOID__POINTER
, G_TYPE_NONE
, 1,
2362 purple_signal_register(handle
, "file-recv-complete",
2363 purple_marshal_VOID__POINTER
, G_TYPE_NONE
, 1,
2365 purple_signal_register(handle
, "file-recv-request",
2366 purple_marshal_VOID__POINTER
, G_TYPE_NONE
, 1,
2371 purple_xfers_uninit(void)
2373 void *handle
= purple_xfers_get_handle();
2375 purple_signals_disconnect_by_handle(handle
);
2376 purple_signals_unregister_by_instance(handle
);
2380 purple_xfers_set_ui_ops(PurpleXferUiOps
*ops
) {
2385 purple_xfers_get_ui_ops(void) {
2389 /**************************************************************************
2391 **************************************************************************/
2392 static PurpleXferUiOps
*
2393 purple_xfer_ui_ops_copy(PurpleXferUiOps
*ops
)
2395 PurpleXferUiOps
*ops_new
;
2397 g_return_val_if_fail(ops
!= NULL
, NULL
);
2399 ops_new
= g_new(PurpleXferUiOps
, 1);
2406 purple_xfer_ui_ops_get_type(void)
2408 static GType type
= 0;
2411 type
= g_boxed_type_register_static("PurpleXferUiOps",
2412 (GBoxedCopyFunc
)purple_xfer_ui_ops_copy
,
2413 (GBoxedFreeFunc
)g_free
);
2419 /**************************************************************************
2420 * PurpleXferProtocolInterface
2421 **************************************************************************/
2422 G_DEFINE_INTERFACE(PurpleProtocolXfer
, purple_protocol_xfer
, G_TYPE_INVALID
);
2425 purple_protocol_xfer_default_init(PurpleProtocolXferInterface
*face
) {
2429 purple_protocol_xfer_can_receive(PurpleProtocolXfer
*prplxfer
,
2430 PurpleConnection
*connection
,
2433 PurpleProtocolXferInterface
*iface
= NULL
;
2435 g_return_val_if_fail(PURPLE_IS_PROTOCOL_XFER(prplxfer
), FALSE
);
2436 g_return_val_if_fail(PURPLE_IS_CONNECTION(connection
), FALSE
);
2437 g_return_val_if_fail(who
, FALSE
);
2439 iface
= PURPLE_PROTOCOL_XFER_GET_IFACE(prplxfer
);
2440 if(iface
&& iface
->can_receive
)
2441 return iface
->can_receive(prplxfer
, connection
, who
);
2443 /* If the PurpleProtocolXfer doesn't implement this function, we assume
2444 * there are no conditions where we can't send a file to the given user.
2450 purple_protocol_xfer_send_file(PurpleProtocolXfer
*prplxfer
,
2451 PurpleConnection
*connection
,
2453 const gchar
*filename
2455 PurpleProtocolXferInterface
*iface
= NULL
;
2457 g_return_if_fail(PURPLE_IS_PROTOCOL_XFER(prplxfer
));
2458 g_return_if_fail(PURPLE_IS_CONNECTION(connection
));
2459 g_return_if_fail(who
);
2461 iface
= PURPLE_PROTOCOL_XFER_GET_IFACE(prplxfer
);
2462 if(iface
&& iface
->send_file
)
2463 iface
->send_file(prplxfer
, connection
, who
, filename
);
2467 purple_protocol_xfer_new_xfer(PurpleProtocolXfer
*prplxfer
,
2468 PurpleConnection
*connection
,
2471 PurpleProtocolXferInterface
*iface
= NULL
;
2473 g_return_val_if_fail(PURPLE_IS_PROTOCOL_XFER(prplxfer
), FALSE
);
2474 g_return_val_if_fail(PURPLE_IS_CONNECTION(connection
), FALSE
);
2475 g_return_val_if_fail(who
, FALSE
);
2477 iface
= PURPLE_PROTOCOL_XFER_GET_IFACE(prplxfer
);
2478 if(iface
&& iface
->new_xfer
)
2479 return iface
->new_xfer(prplxfer
, connection
, who
);