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