Remove unused Meson option for enchant.
[pidgin-git.git] / libpurple / xfer.c
blobff8d4a99c4e9996c4109af94204e1aa969c0f613
1 /* purple
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
5 * source distribution.
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
22 #include "internal.h"
23 #include "glibcompat.h"
25 #include "enums.h"
26 #include "image-store.h"
27 #include "xfer.h"
28 #include "network.h"
29 #include "notify.h"
30 #include "prefs.h"
31 #include "proxy.h"
32 #include "request.h"
33 #include "util.h"
34 #include "debug.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;
42 static GList *xfers;
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
51 transfer. */
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.
84 enum {
85 PURPLE_XFER_READY_NONE = 0x0,
86 PURPLE_XFER_READY_UI = 0x1,
87 PURPLE_XFER_READY_PROTOCOL = 0x2,
88 } ready;
90 /* TODO: Should really use a PurpleCircBuffer for this. */
91 GByteArray *buffer;
93 gpointer thumbnail_data; /* thumbnail image */
94 gsize thumbnail_size;
95 gchar *thumbnail_mimetype;
98 /* GObject property enums */
99 enum
101 PROP_0,
102 PROP_TYPE,
103 PROP_ACCOUNT,
104 PROP_REMOTE_USER,
105 PROP_MESSAGE,
106 PROP_FILENAME,
107 PROP_LOCAL_FILENAME,
108 PROP_FILE_SIZE,
109 PROP_REMOTE_IP,
110 PROP_LOCAL_PORT,
111 PROP_REMOTE_PORT,
112 PROP_FD,
113 PROP_WATCHER,
114 PROP_BYTES_SENT,
115 PROP_START_TIME,
116 PROP_END_TIME,
117 PROP_STATUS,
118 PROP_UI_DATA,
119 PROP_LAST
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);
128 static const gchar *
129 purple_xfer_status_type_to_string(PurpleXferStatus type)
131 static const struct {
132 PurpleXferStatus type;
133 const char *name;
134 } type_names[] = {
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" }
143 gsize i;
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";
152 void
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)
167 return;
169 priv->status = status;
171 g_object_notify_by_pspec(G_OBJECT(xfer), properties[PROP_STATUS]);
173 if(priv->type == PURPLE_XFER_TYPE_SEND) {
174 switch(status) {
175 case PURPLE_XFER_STATUS_ACCEPTED:
176 purple_signal_emit(purple_xfers_get_handle(), "file-send-accept", xfer);
177 break;
178 case PURPLE_XFER_STATUS_STARTED:
179 purple_signal_emit(purple_xfers_get_handle(), "file-send-start", xfer);
180 break;
181 case PURPLE_XFER_STATUS_DONE:
182 purple_signal_emit(purple_xfers_get_handle(), "file-send-complete", xfer);
183 break;
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);
187 break;
188 default:
189 break;
191 } else if(priv->type == PURPLE_XFER_TYPE_RECEIVE) {
192 switch(status) {
193 case PURPLE_XFER_STATUS_ACCEPTED:
194 purple_signal_emit(purple_xfers_get_handle(), "file-recv-accept", xfer);
195 break;
196 case PURPLE_XFER_STATUS_STARTED:
197 purple_signal_emit(purple_xfers_get_handle(), "file-recv-start", xfer);
198 break;
199 case PURPLE_XFER_STATUS_DONE:
200 purple_signal_emit(purple_xfers_get_handle(), "file-recv-complete", xfer);
201 break;
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);
205 break;
206 default:
207 break;
212 static void
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;
218 char *escaped;
219 gconstpointer thumbnail_data;
220 gsize size;
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));
228 if (im == NULL)
229 return;
231 escaped = g_markup_escape_text(message, -1);
233 if (is_error)
234 flags |= PURPLE_MESSAGE_ERROR;
236 if (print_thumbnail && thumbnail_data) {
237 gchar *message_with_img;
238 gpointer data = g_memdup(thumbnail_data, size);
239 PurpleImage *img;
240 guint id;
242 img = purple_image_new_from_data(data, size);
243 id = purple_image_store_add(img);
244 g_object_unref(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);
251 } else {
252 purple_conversation_write_system_message(
253 PURPLE_CONVERSATION(im), escaped, flags);
255 g_free(escaped);
258 void
259 purple_xfer_conversation_write(PurpleXfer *xfer, const gchar *message,
260 gboolean is_error)
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? */
269 static void
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)
279 int err = errno;
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);
286 switch(xfer_type) {
287 case PURPLE_XFER_TYPE_SEND:
288 msg = g_strdup_printf(_("Error reading %s: \n%s.\n"),
289 utf8, g_strerror(err));
290 break;
291 case PURPLE_XFER_TYPE_RECEIVE:
292 msg = g_strdup_printf(_("Error writing %s: \n%s.\n"),
293 utf8, g_strerror(err));
294 break;
295 default:
296 msg = g_strdup_printf(_("Error accessing %s: \n%s.\n"),
297 utf8, g_strerror(err));
298 break;
300 g_free(utf8);
302 purple_xfer_conversation_write(xfer, msg, TRUE);
303 purple_xfer_error(xfer_type, account, priv->who, msg);
304 g_free(msg);
307 static void
308 purple_xfer_choose_file_ok_cb(void *user_data, const char *filename)
310 PurpleXfer *xfer;
311 PurpleXferType type;
312 GStatBuf st;
313 gchar *dir;
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) {
321 #ifndef _WIN32
322 int mode = W_OK;
323 #else
324 int mode = F_OK;
325 #endif
326 dir = g_path_get_dirname(filename);
328 if (g_access(dir, mode) == 0) {
329 purple_xfer_request_accepted(xfer, filename);
330 } else {
331 g_object_ref(xfer);
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);
340 g_free(dir);
342 else {
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)) {
367 char *msg, *utf8;
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);
371 g_free(utf8);
372 purple_notify_error(NULL, NULL, msg, NULL,
373 purple_request_cpar_from_account(
374 purple_xfer_get_account(xfer)));
375 g_free(msg);
376 purple_xfer_request_denied(xfer);
378 else if (type == PURPLE_XFER_TYPE_SEND) {
379 #ifndef _WIN32
380 int mode = R_OK;
381 #else
382 int mode = F_OK;
383 #endif
385 if (g_access(filename, mode) == 0) {
386 purple_xfer_request_accepted(xfer, filename);
387 } else {
388 g_object_ref(xfer);
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);
397 else {
398 purple_xfer_request_accepted(xfer, filename);
401 g_object_unref(xfer);
404 static void
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);
412 else
413 purple_xfer_request_denied(xfer);
414 g_object_unref(xfer);
417 static int
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)),
425 xfer);
427 return 0;
430 static int
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);
437 return 0;
440 static void
441 purple_xfer_ask_recv(PurpleXfer *xfer)
443 PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
444 char *buf, *size_buf;
445 goffset size;
446 gconstpointer thumb;
447 gsize thumb_size;
449 /* If we have already accepted the request, ask the destination file
450 name directly */
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);
462 g_free(size_buf);
464 else
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,
477 thumb_size);
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));
485 g_free(buf);
486 } else
487 purple_xfer_choose_file(xfer);
490 static int
491 ask_accept_ok(PurpleXfer *xfer)
493 purple_xfer_request_accepted(xfer, NULL);
495 return 0;
498 static int
499 ask_accept_cancel(PurpleXfer *xfer)
501 purple_xfer_request_denied(xfer);
502 g_object_unref(xfer);
504 return 0;
507 static void
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));
526 g_free(buf);
527 g_free(buf2);
530 void
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 */
538 g_object_ref(xfer);
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);
558 g_free(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);
564 else
566 purple_xfer_ask_accept(xfer);
569 else
571 purple_xfer_choose_file(xfer);
575 void
576 purple_xfer_request_accepted(PurpleXfer *xfer, const gchar *filename)
578 PurpleXferClass *klass = NULL;
579 PurpleXferPrivate *priv = NULL;
580 GStatBuf st;
581 gchar *msg, *utf8, *base;
582 PurpleBuddy *buddy;
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);
594 klass->init(xfer);
595 } else {
596 purple_debug_warning(
597 "xfer",
598 "can not set file transfer without a file name"
602 return;
605 buddy = purple_blist_find_buddy(priv->account, priv->who);
607 if (priv->type == PURPLE_XFER_TYPE_SEND) {
608 /* Sending a file */
609 /* Check the filename. */
610 PurpleXferUiOps *ui_ops;
611 ui_ops = purple_xfer_get_ui_ops(xfer);
613 #ifdef _WIN32
614 if (g_strrstr(filename, "../") || g_strrstr(filename, "..\\"))
615 #else
616 if (g_strrstr(filename, "../"))
617 #endif
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);
623 g_free(utf8);
624 g_free(msg);
626 g_object_unref(xfer);
627 return;
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);
634 return;
637 purple_xfer_set_local_filename(xfer, filename);
638 purple_xfer_set_size(xfer, st.st_size);
639 } else {
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);
645 g_free(base);
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);
650 g_free(utf8);
651 purple_xfer_conversation_write(xfer, msg, FALSE);
652 g_free(msg);
654 else {
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);
662 g_free(msg);
665 purple_xfer_add(xfer);
667 klass->init(xfer);
670 void
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);
695 return priv->fd;
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;
708 PurpleXferType
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);
716 return priv->type;
719 PurpleAccount *
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;
730 void
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);
738 g_free(priv->who);
739 priv->who = g_strdup(who);
741 g_object_notify_by_pspec(G_OBJECT(xfer), properties[PROP_REMOTE_USER]);
744 const char *
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);
752 return priv->who;
755 PurpleXferStatus
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);
763 return priv->status;
766 gboolean
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))
773 return TRUE;
774 else
775 return FALSE;
778 gboolean
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);
786 const char *
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;
797 const char *
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;
808 goffset
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;
819 goffset
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;
830 goffset
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);
838 return priv->size;
841 double
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)
847 return 0.0;
849 return ((double)purple_xfer_get_bytes_sent(xfer) /
850 (double)purple_xfer_get_size(xfer));
853 guint16
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;
864 const char *
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;
875 guint16
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;
886 gint64
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;
897 gint64
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);
915 priv->fd = fd;
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]);
932 void
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) {
943 char *msg = NULL;
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"),
956 local, filename);
957 g_free(local);
959 else
960 msg = g_strdup_printf(_("Transfer of file %s complete"),
961 filename);
962 g_free(filename);
964 else
965 msg = g_strdup(_("File transfer complete"));
967 im = purple_conversations_find_im_with_account(priv->who,
968 purple_xfer_get_account(xfer));
970 if (im != NULL) {
971 purple_conversation_write_system_message(
972 PURPLE_CONVERSATION(im), msg, 0);
974 g_free(msg);
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));
983 void
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]);
1000 const char *
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;
1011 void
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]);
1028 void
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]);
1045 void
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);
1053 priv->size = size;
1055 g_object_notify_by_pspec(G_OBJECT(xfer), properties[PROP_FILE_SIZE]);
1058 void
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]);
1071 void
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]);
1084 PurpleXferUiOps *
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;
1095 static void
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);
1104 static gssize
1105 do_read(PurpleXfer *xfer, guchar **buffer, gsize size)
1107 PurpleXferPrivate *priv = NULL;
1108 gssize r;
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) {
1119 r = 0;
1120 } else if (r < 0) {
1121 r = -1;
1122 } else if (r == 0) {
1123 r = -1;
1126 return r;
1129 gssize
1130 purple_xfer_read(PurpleXfer *xfer, guchar **buffer)
1132 PurpleXferPrivate *priv = NULL;
1133 PurpleXferClass *klass = NULL;
1134 gsize s;
1135 gssize r;
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;
1144 } else {
1145 s = MIN(
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);
1154 } else {
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
1162 * bigger.
1164 purple_xfer_increase_buffer_size(xfer);
1167 return r;
1170 static gssize
1171 do_write(PurpleXfer *xfer, const guchar *buffer, gsize size)
1173 PurpleXferPrivate *priv = NULL;
1174 gssize r;
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)
1184 r = 0;
1186 return r;
1189 gssize
1190 purple_xfer_write(PurpleXfer *xfer, const guchar *buffer, gsize size)
1192 PurpleXferClass *klass = NULL;
1193 gssize s;
1195 g_return_val_if_fail(PURPLE_IS_XFER(xfer), 0);
1197 s = MIN(
1198 (gssize)purple_xfer_get_bytes_remaining(xfer),
1199 (gssize)size
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);
1210 gboolean
1211 purple_xfer_write_file(PurpleXfer *xfer, const guchar *buffer, gsize size)
1213 PurpleXferPrivate *priv = NULL;
1214 PurpleXferUiOps *ui_ops;
1215 gsize wc;
1216 goffset remaining;
1217 gboolean fs_known;
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));
1232 size = remaining;
1235 if (ui_ops && ui_ops->ui_write) {
1236 wc = ui_ops->ui_write(xfer, buffer, size);
1237 } else {
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);
1242 return FALSE;
1244 wc = fwrite(buffer, 1, size, priv->dest_fp);
1247 if (wc != size) {
1248 purple_debug_error("xfer",
1249 "Unable to write whole buffer.\n");
1250 purple_xfer_cancel_local(xfer);
1251 return FALSE;
1254 purple_xfer_set_bytes_sent(
1255 xfer,
1256 purple_xfer_get_bytes_sent(xfer) + size
1259 return TRUE;
1262 gssize
1263 purple_xfer_read_file(PurpleXfer *xfer, guchar *buffer, gsize size)
1265 PurpleXferPrivate *priv = NULL;
1266 PurpleXferUiOps *ui_ops;
1267 gssize got_len;
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) {
1281 g_free(buffer_got);
1282 purple_debug_error("xfer",
1283 "Got too much data from UI.\n");
1284 purple_xfer_cancel_local(xfer);
1285 return -1;
1288 if (got_len > 0)
1289 memcpy(buffer, buffer_got, got_len);
1290 g_free(buffer_got);
1291 } else {
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);
1296 return -1;
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);
1305 return -1;
1309 if (got_len > 0) {
1310 purple_xfer_set_bytes_sent(xfer,
1311 purple_xfer_get_bytes_sent(xfer) + got_len);
1314 return got_len;
1317 static void
1318 do_transfer(PurpleXfer *xfer)
1320 PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
1321 PurpleXferUiOps *ui_ops;
1322 guchar *buffer = NULL;
1323 gssize r = 0;
1325 ui_ops = purple_xfer_get_ui_ops(xfer);
1327 if (priv->type == PURPLE_XFER_TYPE_RECEIVE) {
1328 r = purple_xfer_read(xfer, &buffer);
1329 if (r > 0) {
1330 if (!purple_xfer_write_file(xfer, buffer, r)) {
1331 g_free(buffer);
1332 return;
1335 } else if(r < 0) {
1336 purple_xfer_cancel_remote(xfer);
1337 g_free(buffer);
1338 return;
1340 } else if (priv->type == PURPLE_XFER_TYPE_SEND) {
1341 gssize result = 0;
1342 gsize s = MIN(
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. */
1350 if (s == 0) {
1351 if (priv->watcher) {
1352 purple_input_remove(priv->watcher);
1353 purple_xfer_set_watcher(xfer, 0);
1355 return;
1358 if (priv->buffer) {
1359 if (priv->buffer->len < s) {
1360 s -= priv->buffer->len;
1361 read = TRUE;
1362 } else {
1363 read = FALSE;
1367 if (read) {
1368 buffer = g_new(guchar, s);
1369 result = purple_xfer_read_file(xfer, buffer, s);
1370 if (result == 0) {
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_free(buffer);
1385 g_return_if_reached();
1387 if (result < 0) {
1388 g_free(buffer);
1389 return;
1393 if (priv->buffer) {
1394 g_byte_array_append(priv->buffer, buffer, result);
1395 g_free(buffer);
1396 buffer = priv->buffer->data;
1397 result = priv->buffer->len;
1400 r = do_write(xfer, buffer, result);
1402 if (r == -1) {
1403 purple_debug_error("xfer", "do_write failed! %s\n", g_strerror(errno));
1404 purple_xfer_cancel_remote(xfer);
1405 if (!priv->buffer)
1406 /* We don't free buffer if priv->buffer is set, because in
1407 that case buffer doesn't belong to us. */
1408 g_free(buffer);
1409 return;
1410 } else if (r == result) {
1412 * We managed to write the entire buffer. This means our
1413 * network is fast and our buffer is too small, so make it
1414 * bigger.
1416 purple_xfer_increase_buffer_size(xfer);
1417 } else {
1418 if (ui_ops && ui_ops->data_not_sent)
1419 ui_ops->data_not_sent(xfer, buffer + r, result - r);
1422 if (priv->buffer) {
1424 * Remove what we wrote
1425 * If we wrote the whole buffer the byte array will be empty
1426 * Otherwise we'll keep what wasn't sent for next time.
1428 buffer = NULL;
1429 g_byte_array_remove_range(priv->buffer, 0, r);
1433 if (r > 0) {
1434 PurpleXferClass *klass = PURPLE_XFER_GET_CLASS(xfer);
1436 if (klass && klass->ack)
1437 klass->ack(xfer, buffer, r);
1439 if (ui_ops != NULL && ui_ops->update_progress != NULL)
1440 ui_ops->update_progress(xfer,
1441 purple_xfer_get_progress(xfer));
1444 g_free(buffer);
1446 if (purple_xfer_get_bytes_sent(xfer) >= purple_xfer_get_size(xfer) &&
1447 !purple_xfer_is_completed(xfer)) {
1448 purple_xfer_set_completed(xfer, TRUE);
1451 /* TODO: Check if above is the only place xfers are marked completed.
1452 * If so, merge these conditions.
1454 if (purple_xfer_is_completed(xfer)) {
1455 purple_xfer_end(xfer);
1459 static void
1460 transfer_cb(gpointer data, gint source, PurpleInputCondition condition)
1462 PurpleXfer *xfer = data;
1463 PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
1465 if (priv->dest_fp == NULL) {
1466 /* The UI is moderating its side manually */
1467 if (0 == (priv->ready & PURPLE_XFER_READY_UI)) {
1468 priv->ready |= PURPLE_XFER_READY_PROTOCOL;
1470 purple_input_remove(priv->watcher);
1471 purple_xfer_set_watcher(xfer, 0);
1473 purple_debug_misc("xfer", "Protocol is ready on ft %p, waiting for UI\n", xfer);
1474 return;
1477 priv->ready = PURPLE_XFER_READY_NONE;
1480 do_transfer(xfer);
1483 static void
1484 begin_transfer(PurpleXfer *xfer, PurpleInputCondition cond)
1486 PurpleXferClass *klass = PURPLE_XFER_GET_CLASS(xfer);
1487 PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
1488 PurpleXferUiOps *ui_ops = purple_xfer_get_ui_ops(xfer);
1490 if (priv->start_time != 0) {
1491 purple_debug_error("xfer", "Transfer is being started multiple times\n");
1492 g_return_if_reached();
1495 if (ui_ops == NULL || (ui_ops->ui_read == NULL && ui_ops->ui_write == NULL)) {
1496 priv->dest_fp = g_fopen(purple_xfer_get_local_filename(xfer),
1497 priv->type == PURPLE_XFER_TYPE_RECEIVE ? "wb" : "rb");
1499 if (priv->dest_fp == NULL) {
1500 purple_xfer_show_file_error(xfer, purple_xfer_get_local_filename(xfer));
1501 purple_xfer_cancel_local(xfer);
1502 return;
1505 if (fseek(priv->dest_fp, priv->bytes_sent, SEEK_SET) != 0) {
1506 purple_debug_error("xfer", "couldn't seek\n");
1507 purple_xfer_show_file_error(xfer, purple_xfer_get_local_filename(xfer));
1508 purple_xfer_cancel_local(xfer);
1509 return;
1513 if (priv->fd != -1) {
1514 purple_xfer_set_watcher(
1515 xfer,
1516 purple_input_add(priv->fd, cond, transfer_cb, xfer)
1520 priv->start_time = g_get_monotonic_time();
1522 g_object_notify_by_pspec(G_OBJECT(xfer), properties[PROP_START_TIME]);
1524 if (klass && klass->start) {
1525 klass->start(xfer);
1529 static void
1530 connect_cb(gpointer data, gint source, const gchar *error_message)
1532 PurpleXfer *xfer = PURPLE_XFER(data);
1534 if (source < 0) {
1535 purple_xfer_cancel_local(xfer);
1536 return;
1539 purple_xfer_set_fd(xfer, source);
1540 begin_transfer(xfer, PURPLE_INPUT_READ);
1543 void
1544 purple_xfer_ui_ready(PurpleXfer *xfer)
1546 PurpleXferPrivate *priv = NULL;
1547 PurpleInputCondition cond = 0;
1549 g_return_if_fail(PURPLE_IS_XFER(xfer));
1551 priv = purple_xfer_get_instance_private(xfer);
1553 priv->ready |= PURPLE_XFER_READY_UI;
1555 if (0 == (priv->ready & PURPLE_XFER_READY_PROTOCOL)) {
1556 purple_debug_misc("xfer", "UI is ready on ft %p, waiting for protocol\n", xfer);
1557 return;
1560 purple_debug_misc("xfer", "UI (and protocol) ready on ft %p, so proceeding\n", xfer);
1562 if (priv->type == PURPLE_XFER_TYPE_SEND) {
1563 cond = PURPLE_INPUT_WRITE;
1564 } else if (priv->type == PURPLE_XFER_TYPE_RECEIVE) {
1565 cond = PURPLE_INPUT_READ;
1568 if (priv->watcher == 0 && priv->fd != -1) {
1569 purple_xfer_set_watcher(
1570 xfer,
1571 purple_input_add(priv->fd, cond, transfer_cb, xfer)
1575 priv->ready = PURPLE_XFER_READY_NONE;
1577 do_transfer(xfer);
1580 void
1581 purple_xfer_protocol_ready(PurpleXfer *xfer)
1583 PurpleXferPrivate *priv = NULL;
1585 g_return_if_fail(PURPLE_IS_XFER(xfer));
1587 priv = purple_xfer_get_instance_private(xfer);
1589 priv->ready |= PURPLE_XFER_READY_PROTOCOL;
1591 /* I don't think fwrite/fread are ever *not* ready */
1592 if (priv->dest_fp == NULL && 0 == (priv->ready & PURPLE_XFER_READY_UI)) {
1593 purple_debug_misc("xfer", "Protocol is ready on ft %p, waiting for UI\n", xfer);
1594 return;
1597 purple_debug_misc("xfer", "Protocol (and UI) ready on ft %p, so proceeding\n", xfer);
1599 priv->ready = PURPLE_XFER_READY_NONE;
1601 do_transfer(xfer);
1604 void
1605 purple_xfer_start(PurpleXfer *xfer, int fd, const char *ip, guint16 port)
1607 PurpleXferPrivate *priv = NULL;
1608 PurpleInputCondition cond;
1609 GObject *obj;
1611 g_return_if_fail(PURPLE_IS_XFER(xfer));
1613 priv = purple_xfer_get_instance_private(xfer);
1615 purple_xfer_set_status(xfer, PURPLE_XFER_STATUS_STARTED);
1617 if (priv->type == PURPLE_XFER_TYPE_RECEIVE) {
1618 cond = PURPLE_INPUT_READ;
1620 if (ip != NULL) {
1621 priv->remote_ip = g_strdup(ip);
1622 priv->remote_port = port;
1624 obj = G_OBJECT(xfer);
1625 g_object_freeze_notify(obj);
1626 g_object_notify_by_pspec(obj, properties[PROP_REMOTE_IP]);
1627 g_object_notify_by_pspec(obj, properties[PROP_REMOTE_PORT]);
1628 g_object_thaw_notify(obj);
1630 /* Establish a file descriptor. */
1631 purple_proxy_connect(
1632 NULL,
1633 priv->account,
1634 priv->remote_ip,
1635 priv->remote_port,
1636 connect_cb,
1637 xfer
1640 return;
1641 } else {
1642 purple_xfer_set_fd(xfer, fd);
1644 } else {
1645 cond = PURPLE_INPUT_WRITE;
1647 purple_xfer_set_fd(xfer, fd);
1650 begin_transfer(xfer, cond);
1653 void
1654 purple_xfer_end(PurpleXfer *xfer)
1656 PurpleXferClass *klass = NULL;
1657 PurpleXferPrivate *priv = NULL;
1659 g_return_if_fail(PURPLE_IS_XFER(xfer));
1661 klass = PURPLE_XFER_GET_CLASS(xfer);
1662 priv = purple_xfer_get_instance_private(xfer);
1664 /* See if we are actually trying to cancel this. */
1665 if (!purple_xfer_is_completed(xfer)) {
1666 purple_xfer_cancel_local(xfer);
1667 return;
1670 priv->end_time = g_get_monotonic_time();
1672 g_object_notify_by_pspec(G_OBJECT(xfer), properties[PROP_END_TIME]);
1674 if (klass && klass->end != NULL) {
1675 klass->end(xfer);
1678 if (priv->watcher != 0) {
1679 purple_input_remove(priv->watcher);
1680 purple_xfer_set_watcher(xfer, 0);
1683 if (priv->fd != -1) {
1684 if (close(priv->fd)) {
1685 purple_debug_error("xfer", "closing file descr in purple_xfer_end() failed: %s",
1686 g_strerror(errno));
1690 if (priv->dest_fp != NULL) {
1691 if (fclose(priv->dest_fp)) {
1692 purple_debug_error("xfer", "closing dest file in purple_xfer_end() failed: %s",
1693 g_strerror(errno));
1695 priv->dest_fp = NULL;
1698 g_object_unref(xfer);
1701 void
1702 purple_xfer_add(PurpleXfer *xfer)
1704 PurpleXferUiOps *ui_ops;
1706 g_return_if_fail(PURPLE_IS_XFER(xfer));
1708 ui_ops = purple_xfer_get_ui_ops(xfer);
1710 if (ui_ops != NULL && ui_ops->add_xfer != NULL)
1711 ui_ops->add_xfer(xfer);
1714 void
1715 purple_xfer_cancel_local(PurpleXfer *xfer)
1717 PurpleXferClass *klass = NULL;
1718 PurpleXferPrivate *priv = NULL;
1719 PurpleXferUiOps *ui_ops;
1720 char *msg = NULL;
1722 g_return_if_fail(PURPLE_IS_XFER(xfer));
1724 klass = PURPLE_XFER_GET_CLASS(xfer);
1725 priv = purple_xfer_get_instance_private(xfer);
1727 /* TODO: We definitely want to close any open request dialogs associated
1728 with this transfer. However, in some cases the request dialog might
1729 own a reference on the xfer. This happens at least with the "%s wants
1730 to send you %s" dialog from purple_xfer_ask_recv(). In these cases
1731 the ref count will not be decremented when the request dialog is
1732 closed, so the ref count will never reach 0 and the xfer will never
1733 be freed. This is a memleak and should be fixed. It's not clear what
1734 the correct fix is. Probably requests should have a destroy function
1735 that is called when the request is destroyed. But also, ref counting
1736 xfer objects makes this code REALLY complicated. An alternate fix is
1737 to not ref count and instead just make sure the object still exists
1738 when we try to use it. */
1739 purple_request_close_with_handle(xfer);
1741 purple_xfer_set_status(xfer, PURPLE_XFER_STATUS_CANCEL_LOCAL);
1742 priv->end_time = g_get_monotonic_time();
1744 g_object_notify_by_pspec(G_OBJECT(xfer), properties[PROP_END_TIME]);
1746 if (purple_xfer_get_filename(xfer) != NULL)
1748 msg = g_strdup_printf(_("You cancelled the transfer of %s"),
1749 purple_xfer_get_filename(xfer));
1751 else
1753 msg = g_strdup(_("File transfer cancelled"));
1755 purple_xfer_conversation_write(xfer, msg, FALSE);
1756 g_free(msg);
1758 if (priv->type == PURPLE_XFER_TYPE_SEND)
1760 if (klass && klass->cancel_send) {
1761 klass->cancel_send(xfer);
1763 } else {
1764 if (klass && klass->cancel_recv) {
1765 klass->cancel_recv(xfer);
1769 if (priv->watcher != 0) {
1770 purple_input_remove(priv->watcher);
1771 purple_xfer_set_watcher(xfer, 0);
1774 if (priv->fd != -1) {
1775 close(priv->fd);
1778 if (priv->dest_fp != NULL) {
1779 fclose(priv->dest_fp);
1780 priv->dest_fp = NULL;
1783 ui_ops = purple_xfer_get_ui_ops(xfer);
1785 if (ui_ops != NULL && ui_ops->cancel_local != NULL) {
1786 ui_ops->cancel_local(xfer);
1789 g_object_unref(xfer);
1792 void
1793 purple_xfer_cancel_remote(PurpleXfer *xfer)
1795 PurpleXferClass *klass = NULL;
1796 PurpleXferPrivate *priv = NULL;
1797 PurpleXferUiOps *ui_ops;
1798 gchar *msg;
1799 PurpleAccount *account;
1800 PurpleBuddy *buddy;
1802 g_return_if_fail(PURPLE_IS_XFER(xfer));
1804 klass = PURPLE_XFER_GET_CLASS(xfer);
1805 priv = purple_xfer_get_instance_private(xfer);
1807 purple_request_close_with_handle(xfer);
1808 purple_xfer_set_status(xfer, PURPLE_XFER_STATUS_CANCEL_REMOTE);
1809 priv->end_time = g_get_monotonic_time();
1811 g_object_notify_by_pspec(G_OBJECT(xfer), properties[PROP_END_TIME]);
1813 account = purple_xfer_get_account(xfer);
1814 buddy = purple_blist_find_buddy(account, priv->who);
1816 if (purple_xfer_get_filename(xfer) != NULL)
1818 msg = g_strdup_printf(_("%s cancelled the transfer of %s"),
1819 buddy ? purple_buddy_get_alias(buddy) : priv->who, purple_xfer_get_filename(xfer));
1821 else
1823 msg = g_strdup_printf(_("%s cancelled the file transfer"),
1824 buddy ? purple_buddy_get_alias(buddy) : priv->who);
1826 purple_xfer_conversation_write(xfer, msg, TRUE);
1827 purple_xfer_error(purple_xfer_get_xfer_type(xfer), account, priv->who, msg);
1828 g_free(msg);
1830 if (priv->type == PURPLE_XFER_TYPE_SEND) {
1831 if (klass && klass->cancel_send) {
1832 klass->cancel_send(xfer);
1834 } else if(priv->type == PURPLE_XFER_TYPE_RECEIVE) {
1835 if (klass && klass->cancel_recv) {
1836 klass->cancel_recv(xfer);
1840 if (priv->watcher != 0) {
1841 purple_input_remove(priv->watcher);
1842 purple_xfer_set_watcher(xfer, 0);
1845 if (priv->fd != -1)
1846 close(priv->fd);
1848 if (priv->dest_fp != NULL) {
1849 fclose(priv->dest_fp);
1850 priv->dest_fp = NULL;
1853 ui_ops = purple_xfer_get_ui_ops(xfer);
1855 if (ui_ops != NULL && ui_ops->cancel_remote != NULL)
1856 ui_ops->cancel_remote(xfer);
1858 g_object_unref(xfer);
1861 void
1862 purple_xfer_error(PurpleXferType type, PurpleAccount *account, const gchar *who, const gchar *msg)
1864 gchar *title = NULL;
1866 g_return_if_fail(msg != NULL);
1868 if (account) {
1869 PurpleBuddy *buddy;
1870 buddy = purple_blist_find_buddy(account, who);
1871 if (buddy)
1872 who = purple_buddy_get_alias(buddy);
1875 if (type == PURPLE_XFER_TYPE_SEND) {
1876 title = g_strdup_printf(_("File transfer to %s failed."), who);
1877 } else if (type == PURPLE_XFER_TYPE_RECEIVE) {
1878 title = g_strdup_printf(_("File transfer from %s failed."), who);
1881 purple_notify_error(NULL, NULL, title, msg,
1882 purple_request_cpar_from_account(account));
1884 g_free(title);
1887 void
1888 purple_xfer_update_progress(PurpleXfer *xfer)
1890 PurpleXferUiOps *ui_ops;
1892 g_return_if_fail(PURPLE_IS_XFER(xfer));
1894 ui_ops = purple_xfer_get_ui_ops(xfer);
1895 if (ui_ops != NULL && ui_ops->update_progress != NULL) {
1896 ui_ops->update_progress(xfer, purple_xfer_get_progress(xfer));
1900 gconstpointer
1901 purple_xfer_get_thumbnail(PurpleXfer *xfer, gsize *len)
1903 PurpleXferPrivate *priv = NULL;
1905 g_return_val_if_fail(PURPLE_IS_XFER(xfer), NULL);
1907 priv = purple_xfer_get_instance_private(xfer);
1909 if (len) {
1910 *len = priv->thumbnail_size;
1913 return priv->thumbnail_data;
1916 const gchar *
1917 purple_xfer_get_thumbnail_mimetype(PurpleXfer *xfer)
1919 PurpleXferPrivate *priv = NULL;
1921 g_return_val_if_fail(PURPLE_IS_XFER(xfer), NULL);
1923 priv = purple_xfer_get_instance_private(xfer);
1925 return priv->thumbnail_mimetype;
1928 void
1929 purple_xfer_set_thumbnail(PurpleXfer *xfer, gconstpointer thumbnail,
1930 gsize size, const gchar *mimetype)
1932 PurpleXferPrivate *priv = NULL;
1933 gpointer old_thumbnail_data;
1934 gchar *old_mimetype;
1936 g_return_if_fail(PURPLE_IS_XFER(xfer));
1938 priv = purple_xfer_get_instance_private(xfer);
1940 /* Hold onto these in case they are equal to passed-in pointers */
1941 old_thumbnail_data = priv->thumbnail_data;
1942 old_mimetype = priv->thumbnail_mimetype;
1944 if (thumbnail && size > 0) {
1945 priv->thumbnail_data = g_memdup(thumbnail, size);
1946 priv->thumbnail_size = size;
1947 priv->thumbnail_mimetype = g_strdup(mimetype);
1948 } else {
1949 priv->thumbnail_data = NULL;
1950 priv->thumbnail_size = 0;
1951 priv->thumbnail_mimetype = NULL;
1954 /* Now it's safe to free the pointers */
1955 g_free(old_thumbnail_data);
1956 g_free(old_mimetype);
1959 void
1960 purple_xfer_prepare_thumbnail(PurpleXfer *xfer, const gchar *formats)
1962 PurpleXferUiOps *ui_ops;
1964 g_return_if_fail(PURPLE_IS_XFER(xfer));
1966 ui_ops = purple_xfer_get_ui_ops(xfer);
1967 if (ui_ops && ui_ops->add_thumbnail) {
1968 ui_ops->add_thumbnail(xfer, formats);
1972 void purple_xfer_set_ui_data(PurpleXfer *xfer, gpointer ui_data)
1974 PurpleXferPrivate *priv = NULL;
1976 g_return_if_fail(PURPLE_IS_XFER(xfer));
1978 priv = purple_xfer_get_instance_private(xfer);
1980 priv->ui_data = ui_data;
1982 g_object_notify_by_pspec(G_OBJECT(xfer), properties[PROP_UI_DATA]);
1985 gpointer purple_xfer_get_ui_data(PurpleXfer *xfer)
1987 PurpleXferPrivate *priv = NULL;
1989 g_return_val_if_fail(PURPLE_IS_XFER(xfer), NULL);
1991 priv = purple_xfer_get_instance_private(xfer);
1993 return priv->ui_data;
1996 /**************************************************************************
1997 * GObject code
1998 **************************************************************************/
1999 static void
2000 purple_xfer_set_property(GObject *obj, guint param_id, const GValue *value,
2001 GParamSpec *pspec)
2003 PurpleXfer *xfer = PURPLE_XFER(obj);
2004 PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
2006 switch (param_id) {
2007 case PROP_TYPE:
2008 priv->type = g_value_get_enum(value);
2009 break;
2010 case PROP_ACCOUNT:
2011 priv->account = g_value_get_object(value);
2012 break;
2013 case PROP_REMOTE_USER:
2014 purple_xfer_set_remote_user(xfer, g_value_get_string(value));
2015 break;
2016 case PROP_MESSAGE:
2017 purple_xfer_set_message(xfer, g_value_get_string(value));
2018 break;
2019 case PROP_FILENAME:
2020 purple_xfer_set_filename(xfer, g_value_get_string(value));
2021 break;
2022 case PROP_LOCAL_FILENAME:
2023 purple_xfer_set_local_filename(xfer, g_value_get_string(value));
2024 break;
2025 case PROP_FILE_SIZE:
2026 purple_xfer_set_size(xfer, g_value_get_int64(value));
2027 break;
2028 case PROP_LOCAL_PORT:
2029 purple_xfer_set_local_port(xfer, g_value_get_int(value));
2030 break;
2031 case PROP_FD:
2032 purple_xfer_set_fd(xfer, g_value_get_int(value));
2033 break;
2034 case PROP_WATCHER:
2035 purple_xfer_set_watcher(xfer, g_value_get_int(value));
2036 break;
2037 case PROP_BYTES_SENT:
2038 purple_xfer_set_bytes_sent(xfer, g_value_get_int64(value));
2039 break;
2040 case PROP_STATUS:
2041 purple_xfer_set_status(xfer, g_value_get_enum(value));
2042 break;
2043 case PROP_UI_DATA:
2044 purple_xfer_set_ui_data(xfer, g_value_get_pointer(value));
2045 break;
2046 default:
2047 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
2048 break;
2052 static void
2053 purple_xfer_get_property(GObject *obj, guint param_id, GValue *value,
2054 GParamSpec *pspec)
2056 PurpleXfer *xfer = PURPLE_XFER(obj);
2058 switch (param_id) {
2059 case PROP_TYPE:
2060 g_value_set_enum(value, purple_xfer_get_xfer_type(xfer));
2061 break;
2062 case PROP_ACCOUNT:
2063 g_value_set_object(value, purple_xfer_get_account(xfer));
2064 break;
2065 case PROP_REMOTE_USER:
2066 g_value_set_string(value, purple_xfer_get_remote_user(xfer));
2067 break;
2068 case PROP_MESSAGE:
2069 g_value_set_string(value, purple_xfer_get_message(xfer));
2070 break;
2071 case PROP_FILENAME:
2072 g_value_set_string(value, purple_xfer_get_filename(xfer));
2073 break;
2074 case PROP_LOCAL_FILENAME:
2075 g_value_set_string(value, purple_xfer_get_local_filename(xfer));
2076 break;
2077 case PROP_FILE_SIZE:
2078 g_value_set_int64(value, purple_xfer_get_size(xfer));
2079 break;
2080 case PROP_REMOTE_IP:
2081 g_value_set_string(value, purple_xfer_get_remote_ip(xfer));
2082 break;
2083 case PROP_LOCAL_PORT:
2084 g_value_set_int(value, purple_xfer_get_local_port(xfer));
2085 break;
2086 case PROP_REMOTE_PORT:
2087 g_value_set_int(value, purple_xfer_get_remote_port(xfer));
2088 break;
2089 case PROP_FD:
2090 g_value_set_int(value, purple_xfer_get_fd(xfer));
2091 break;
2092 case PROP_WATCHER:
2093 g_value_set_int(value, purple_xfer_get_watcher(xfer));
2094 break;
2095 case PROP_BYTES_SENT:
2096 g_value_set_int64(value, purple_xfer_get_bytes_sent(xfer));
2097 break;
2098 case PROP_START_TIME:
2099 g_value_set_int64(value, purple_xfer_get_start_time(xfer));
2100 break;
2101 case PROP_END_TIME:
2102 g_value_set_int64(value, purple_xfer_get_end_time(xfer));
2103 break;
2104 case PROP_STATUS:
2105 g_value_set_enum(value, purple_xfer_get_status(xfer));
2106 break;
2107 case PROP_UI_DATA:
2108 g_value_set_pointer(value, purple_xfer_get_ui_data(xfer));
2109 break;
2110 default:
2111 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
2112 break;
2116 static void
2117 purple_xfer_init(PurpleXfer *xfer)
2119 PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
2121 priv->ui_ops = purple_xfers_get_ui_ops();
2122 priv->current_buffer_size = FT_INITIAL_BUFFER_SIZE;
2123 priv->fd = -1;
2124 priv->ready = PURPLE_XFER_READY_NONE;
2127 static void
2128 purple_xfer_constructed(GObject *object)
2130 PurpleXfer *xfer = PURPLE_XFER(object);
2131 PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
2132 PurpleXferUiOps *ui_ops;
2134 G_OBJECT_CLASS(purple_xfer_parent_class)->constructed(object);
2136 ui_ops = purple_xfers_get_ui_ops();
2138 if (ui_ops && ui_ops->data_not_sent) {
2139 /* If the ui will handle unsent data no need for buffer */
2140 priv->buffer = NULL;
2141 } else {
2142 priv->buffer = g_byte_array_sized_new(FT_INITIAL_BUFFER_SIZE);
2145 if (ui_ops != NULL && ui_ops->new_xfer != NULL) {
2146 ui_ops->new_xfer(xfer);
2149 xfers = g_list_prepend(xfers, xfer);
2152 static void
2153 purple_xfer_finalize(GObject *object)
2155 PurpleXfer *xfer = PURPLE_XFER(object);
2156 PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
2157 PurpleXferUiOps *ui_ops;
2159 /* Close the file browser, if it's open */
2160 purple_request_close_with_handle(xfer);
2162 if (purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_STARTED) {
2163 purple_xfer_cancel_local(xfer);
2166 ui_ops = purple_xfer_get_ui_ops(xfer);
2168 if (ui_ops != NULL && ui_ops->destroy != NULL) {
2169 ui_ops->destroy(xfer);
2172 xfers = g_list_remove(xfers, xfer);
2174 g_free(priv->who);
2175 g_free(priv->filename);
2176 g_free(priv->remote_ip);
2177 g_free(priv->local_filename);
2179 if (priv->buffer) {
2180 g_byte_array_free(priv->buffer, TRUE);
2183 g_free(priv->thumbnail_data);
2184 g_free(priv->thumbnail_mimetype);
2186 G_OBJECT_CLASS(purple_xfer_parent_class)->finalize(object);
2189 /* Class initializer function */
2190 static void
2191 purple_xfer_class_init(PurpleXferClass *klass)
2193 GObjectClass *obj_class = G_OBJECT_CLASS(klass);
2195 obj_class->finalize = purple_xfer_finalize;
2196 obj_class->constructed = purple_xfer_constructed;
2197 obj_class->get_property = purple_xfer_get_property;
2198 obj_class->set_property = purple_xfer_set_property;
2200 klass->write = do_write;
2201 klass->read = do_read;
2203 properties[PROP_TYPE] = g_param_spec_enum("type", "Transfer type",
2204 "The type of file transfer.", PURPLE_TYPE_XFER_TYPE,
2205 PURPLE_XFER_TYPE_UNKNOWN,
2206 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
2207 G_PARAM_STATIC_STRINGS);
2209 properties[PROP_ACCOUNT] = g_param_spec_object("account", "Account",
2210 "The account sending or receiving the file.",
2211 PURPLE_TYPE_ACCOUNT,
2212 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
2213 G_PARAM_STATIC_STRINGS);
2215 properties[PROP_REMOTE_USER] = g_param_spec_string("remote-user",
2216 "Remote user",
2217 "The name of the remote user.", NULL,
2218 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
2220 properties[PROP_MESSAGE] = g_param_spec_string("message", "Message",
2221 "The message for the file transfer.", NULL,
2222 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
2224 properties[PROP_FILENAME] = g_param_spec_string("filename", "Filename",
2225 "The filename for the file transfer.", NULL,
2226 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
2228 properties[PROP_LOCAL_FILENAME] = g_param_spec_string("local-filename",
2229 "Local filename",
2230 "The local filename for the file transfer.", NULL,
2231 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
2233 properties[PROP_FILE_SIZE] = g_param_spec_int64("file-size", "File size",
2234 "Size of the file in a file transfer.",
2235 G_MININT64, G_MAXINT64, 0,
2236 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
2238 properties[PROP_REMOTE_IP] = g_param_spec_string("remote-ip", "Remote IP",
2239 "The remote IP address in the file transfer.", NULL,
2240 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
2242 properties[PROP_LOCAL_PORT] = g_param_spec_int("local-port", "Local port",
2243 "The local port number in the file transfer.",
2244 G_MININT, G_MAXINT, 0,
2245 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
2247 properties[PROP_REMOTE_PORT] = g_param_spec_int("remote-port",
2248 "Remote port",
2249 "The remote port number in the file transfer.",
2250 G_MININT, G_MAXINT, 0,
2251 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
2253 properties[PROP_FD] = g_param_spec_int("fd", "Socket FD",
2254 "The socket file descriptor.",
2255 G_MININT, G_MAXINT, 0,
2256 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
2258 properties[PROP_WATCHER] = g_param_spec_int("watcher", "Watcher",
2259 "The watcher for the file transfer.",
2260 G_MININT, G_MAXINT, 0,
2261 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
2263 properties[PROP_BYTES_SENT] = g_param_spec_int64("bytes-sent", "Bytes sent",
2264 "The number of bytes sent (or received) so far.",
2265 G_MININT64, G_MAXINT64, 0,
2266 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
2268 properties[PROP_START_TIME] = g_param_spec_int64(
2269 "start-time", "Start time",
2270 "The monotonic time the transfer of a file started.",
2271 G_MININT64, G_MAXINT64, 0,
2272 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
2274 properties[PROP_END_TIME] = g_param_spec_int64(
2275 "end-time", "End time",
2276 "The monotonic time the transfer of a file ended.", G_MININT64,
2277 G_MAXINT64, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
2279 properties[PROP_STATUS] = g_param_spec_enum("status", "Status",
2280 "The current status for the file transfer.",
2281 PURPLE_TYPE_XFER_STATUS, PURPLE_XFER_STATUS_UNKNOWN,
2282 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
2284 properties[PROP_UI_DATA] = g_param_spec_pointer("ui-data", "UI Data",
2285 "The UI specific data for this xfer",
2286 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
2288 g_object_class_install_properties(obj_class, PROP_LAST, properties);
2291 PurpleXfer *
2292 purple_xfer_new(PurpleAccount *account, PurpleXferType type, const char *who)
2294 PurpleXfer *xfer;
2295 PurpleProtocol *protocol;
2297 g_return_val_if_fail(type != PURPLE_XFER_TYPE_UNKNOWN, NULL);
2298 g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
2299 g_return_val_if_fail(who != NULL, NULL);
2301 protocol = purple_protocols_find(purple_account_get_protocol_id(account));
2302 if (PURPLE_IS_PROTOCOL_XFER(protocol)) {
2303 PurpleConnection *connection = purple_account_get_connection(account);
2305 xfer = purple_protocol_xfer_new_xfer(
2306 PURPLE_PROTOCOL_XFER(protocol),
2307 connection,
2308 /* TODO: this should support the type */
2311 } else {
2312 xfer = g_object_new(PURPLE_TYPE_XFER,
2313 "account", account,
2314 "type", type,
2315 "remote-user", who,
2316 NULL
2320 return xfer;
2323 /**************************************************************************
2324 * File Transfer Subsystem API
2325 **************************************************************************/
2326 GList *
2327 purple_xfers_get_all()
2329 return xfers;
2332 void *
2333 purple_xfers_get_handle(void) {
2334 static int handle = 0;
2336 return &handle;
2339 void
2340 purple_xfers_init(void) {
2341 void *handle = purple_xfers_get_handle();
2343 /* register signals */
2344 purple_signal_register(handle, "file-recv-accept",
2345 purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
2346 PURPLE_TYPE_XFER);
2347 purple_signal_register(handle, "file-send-accept",
2348 purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
2349 PURPLE_TYPE_XFER);
2350 purple_signal_register(handle, "file-recv-start",
2351 purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
2352 PURPLE_TYPE_XFER);
2353 purple_signal_register(handle, "file-send-start",
2354 purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
2355 PURPLE_TYPE_XFER);
2356 purple_signal_register(handle, "file-send-cancel",
2357 purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
2358 PURPLE_TYPE_XFER);
2359 purple_signal_register(handle, "file-recv-cancel",
2360 purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
2361 PURPLE_TYPE_XFER);
2362 purple_signal_register(handle, "file-send-complete",
2363 purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
2364 PURPLE_TYPE_XFER);
2365 purple_signal_register(handle, "file-recv-complete",
2366 purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
2367 PURPLE_TYPE_XFER);
2368 purple_signal_register(handle, "file-recv-request",
2369 purple_marshal_VOID__POINTER, G_TYPE_NONE, 1,
2370 PURPLE_TYPE_XFER);
2373 void
2374 purple_xfers_uninit(void)
2376 void *handle = purple_xfers_get_handle();
2378 purple_signals_disconnect_by_handle(handle);
2379 purple_signals_unregister_by_instance(handle);
2382 void
2383 purple_xfers_set_ui_ops(PurpleXferUiOps *ops) {
2384 xfer_ui_ops = ops;
2387 PurpleXferUiOps *
2388 purple_xfers_get_ui_ops(void) {
2389 return xfer_ui_ops;
2392 /**************************************************************************
2393 * GBoxed code
2394 **************************************************************************/
2395 static PurpleXferUiOps *
2396 purple_xfer_ui_ops_copy(PurpleXferUiOps *ops)
2398 PurpleXferUiOps *ops_new;
2400 g_return_val_if_fail(ops != NULL, NULL);
2402 ops_new = g_new(PurpleXferUiOps, 1);
2403 *ops_new = *ops;
2405 return ops_new;
2408 GType
2409 purple_xfer_ui_ops_get_type(void)
2411 static GType type = 0;
2413 if (type == 0) {
2414 type = g_boxed_type_register_static("PurpleXferUiOps",
2415 (GBoxedCopyFunc)purple_xfer_ui_ops_copy,
2416 (GBoxedFreeFunc)g_free);
2419 return type;
2422 /**************************************************************************
2423 * PurpleXferProtocolInterface
2424 **************************************************************************/
2425 G_DEFINE_INTERFACE(PurpleProtocolXfer, purple_protocol_xfer, G_TYPE_INVALID);
2427 static void
2428 purple_protocol_xfer_default_init(PurpleProtocolXferInterface *face) {
2431 gboolean
2432 purple_protocol_xfer_can_receive(PurpleProtocolXfer *prplxfer,
2433 PurpleConnection *connection,
2434 const gchar *who
2436 PurpleProtocolXferInterface *iface = NULL;
2438 g_return_val_if_fail(PURPLE_IS_PROTOCOL_XFER(prplxfer), FALSE);
2439 g_return_val_if_fail(PURPLE_IS_CONNECTION(connection), FALSE);
2440 g_return_val_if_fail(who, FALSE);
2442 iface = PURPLE_PROTOCOL_XFER_GET_IFACE(prplxfer);
2443 if(iface && iface->can_receive)
2444 return iface->can_receive(prplxfer, connection, who);
2446 /* If the PurpleProtocolXfer doesn't implement this function, we assume
2447 * there are no conditions where we can't send a file to the given user.
2449 return TRUE;
2452 void
2453 purple_protocol_xfer_send_file(PurpleProtocolXfer *prplxfer,
2454 PurpleConnection *connection,
2455 const gchar *who,
2456 const gchar *filename
2458 PurpleProtocolXferInterface *iface = NULL;
2460 g_return_if_fail(PURPLE_IS_PROTOCOL_XFER(prplxfer));
2461 g_return_if_fail(PURPLE_IS_CONNECTION(connection));
2462 g_return_if_fail(who);
2464 iface = PURPLE_PROTOCOL_XFER_GET_IFACE(prplxfer);
2465 if(iface && iface->send_file)
2466 iface->send_file(prplxfer, connection, who, filename);
2469 PurpleXfer *
2470 purple_protocol_xfer_new_xfer(PurpleProtocolXfer *prplxfer,
2471 PurpleConnection *connection,
2472 const gchar *who
2474 PurpleProtocolXferInterface *iface = NULL;
2476 g_return_val_if_fail(PURPLE_IS_PROTOCOL_XFER(prplxfer), FALSE);
2477 g_return_val_if_fail(PURPLE_IS_CONNECTION(connection), FALSE);
2478 g_return_val_if_fail(who, FALSE);
2480 iface = PURPLE_PROTOCOL_XFER_GET_IFACE(prplxfer);
2481 if(iface && iface->new_xfer)
2482 return iface->new_xfer(prplxfer, connection, who);
2484 return NULL;