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