* @file xfer.c File Transfer API * Purple is the legal property of its developers, whose names are too numerous * to list here. Please refer to the COPYRIGHT file distributed with this * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA #define FT_INITIAL_BUFFER_SIZE 4096 #define FT_MAX_BUFFER_SIZE 65535 #define PURPLE_XFER_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_XFER, PurpleXferPrivate)) /** @copydoc _PurpleXferPrivate */ typedef struct _PurpleXferPrivate PurpleXferPrivate; static PurpleXferUiOps *xfer_ui_ops = NULL; /** Private data for a file transfer */ struct _PurpleXferPrivate { PurpleXferType type; /**< The type of transfer. */ PurpleAccount *account; /**< The account. */ char *who; /**< The person on the other end of the char *message; /**< A message sent with the request */ char *filename; /**< The name sent over the network. */ char *local_filename; /**< The name on the local hard drive. */ goffset size; /**< The size of the file. */ FILE *dest_fp; /**< The destination file pointer. */ char *remote_ip; /**< The remote IP address. */ guint16 local_port; /**< The local port. */ guint16 remote_port; /**< The remote port. */ int fd; /**< The socket file descriptor. */ int watcher; /**< Watcher. */ goffset bytes_sent; /**< The number of bytes sent. */ goffset bytes_remaining; /**< The number of bytes remaining. */ time_t start_time; /**< When the transfer of data began. */ time_t end_time; /**< When the transfer of data ended. */ size_t current_buffer_size; /**< This gradually increases for fast PurpleXferStatus status; /**< File Transfer's status. */ /** I/O operations, which should be set by the prpl using * purple_xfer_set_init_fnc() and friends. Setting #init is * mandatory; all others are optional. void (*init)(PurpleXfer *xfer); void (*request_denied)(PurpleXfer *xfer); void (*start)(PurpleXfer *xfer); void (*end)(PurpleXfer *xfer); void (*cancel_send)(PurpleXfer *xfer); void (*cancel_recv)(PurpleXfer *xfer); gssize (*read)(guchar **buffer, size_t size, PurpleXfer *xfer); gssize (*write)(const guchar *buffer, size_t size, PurpleXfer *xfer); void (*ack)(PurpleXfer *xfer, const guchar *buffer, size_t size); PurpleXferUiOps *ui_ops; /**< UI-specific operations. */ /* TODO Remove this and use protocol-specific subclasses. */ void *proto_data; /**< prpl-specific data. */ * Used to moderate the file transfer when either the read/write ui_ops are * set or fd is not set. In those cases, the UI/prpl call the respective * function, which is somewhat akin to a fd watch being triggered. PURPLE_XFER_READY_NONE = 0x0, PURPLE_XFER_READY_UI = 0x1, PURPLE_XFER_READY_PRPL = 0x2, /* TODO: Should really use a PurpleCircBuffer for this. */ gpointer thumbnail_data; /**< thumbnail image */ gchar *thumbnail_mimetype; /* GObject property enums */ static GObjectClass *parent_class; static GParamSpec *properties[PROP_LAST]; static int purple_xfer_choose_file(PurpleXfer *xfer); purple_xfer_status_type_to_string(PurpleXferStatus type) { PURPLE_XFER_STATUS_UNKNOWN, "unknown" }, { PURPLE_XFER_STATUS_NOT_STARTED, "not started" }, { PURPLE_XFER_STATUS_ACCEPTED, "accepted" }, { PURPLE_XFER_STATUS_STARTED, "started" }, { PURPLE_XFER_STATUS_DONE, "done" }, { PURPLE_XFER_STATUS_CANCEL_LOCAL, "cancelled locally" }, { PURPLE_XFER_STATUS_CANCEL_REMOTE, "cancelled remotely" } for (i = 0; i < G_N_ELEMENTS(type_names); ++i) if (type_names[i].type == type) return type_names[i].name; purple_xfer_set_status(PurpleXfer *xfer, PurpleXferStatus status) PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); g_return_if_fail(priv != NULL); if (purple_debug_is_verbose()) purple_debug_info("xfer", "Changing status of xfer %p from %s to %s\n", xfer, purple_xfer_status_type_to_string(priv->status), purple_xfer_status_type_to_string(status)); if (priv->status == status) g_object_notify_by_pspec(G_OBJECT(xfer), properties[PROP_STATUS]); if(priv->type == PURPLE_XFER_TYPE_SEND) { case PURPLE_XFER_STATUS_ACCEPTED: purple_signal_emit(purple_xfers_get_handle(), "file-send-accept", xfer); case PURPLE_XFER_STATUS_STARTED: purple_signal_emit(purple_xfers_get_handle(), "file-send-start", xfer); case PURPLE_XFER_STATUS_DONE: purple_signal_emit(purple_xfers_get_handle(), "file-send-complete", xfer); case PURPLE_XFER_STATUS_CANCEL_LOCAL: case PURPLE_XFER_STATUS_CANCEL_REMOTE: purple_signal_emit(purple_xfers_get_handle(), "file-send-cancel", xfer); } else if(priv->type == PURPLE_XFER_TYPE_RECEIVE) { case PURPLE_XFER_STATUS_ACCEPTED: purple_signal_emit(purple_xfers_get_handle(), "file-recv-accept", xfer); case PURPLE_XFER_STATUS_STARTED: purple_signal_emit(purple_xfers_get_handle(), "file-recv-start", xfer); case PURPLE_XFER_STATUS_DONE: purple_signal_emit(purple_xfers_get_handle(), "file-recv-complete", xfer); case PURPLE_XFER_STATUS_CANCEL_LOCAL: case PURPLE_XFER_STATUS_CANCEL_REMOTE: purple_signal_emit(purple_xfers_get_handle(), "file-recv-cancel", xfer); purple_xfer_conversation_write_internal(PurpleXfer *xfer, const char *message, gboolean is_error, gboolean print_thumbnail) PurpleIMConversation *im = NULL; PurpleMessageFlags flags = PURPLE_MESSAGE_SYSTEM; gconstpointer thumbnail_data; PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); g_return_if_fail(priv != NULL); g_return_if_fail(message != NULL); thumbnail_data = purple_xfer_get_thumbnail(xfer, &size); im = purple_conversations_find_im_with_account(priv->who, purple_xfer_get_account(xfer)); escaped = g_markup_escape_text(message, -1); flags |= PURPLE_MESSAGE_ERROR; if (print_thumbnail && thumbnail_data) { gpointer data = g_memdup(thumbnail_data, size); int id = purple_imgstore_new_with_id(data, size, NULL); g_strdup_printf("<img src='" PURPLE_STORED_IMAGE_PROTOCOL "%d'> %s", purple_conversation_write(PURPLE_CONVERSATION(im), NULL, message_with_img, flags, time(NULL)); purple_imgstore_unref_by_id(id); g_free(message_with_img); purple_conversation_write(PURPLE_CONVERSATION(im), NULL, escaped, flags, purple_xfer_conversation_write(PurpleXfer *xfer, const gchar *message, purple_xfer_conversation_write_internal(xfer, message, is_error, FALSE); /* maybe this one should be exported publically? */ purple_xfer_conversation_write_with_thumbnail(PurpleXfer *xfer, purple_xfer_conversation_write_internal(xfer, message, FALSE, TRUE); static void purple_xfer_show_file_error(PurpleXfer *xfer, const char *filename) gchar *msg = NULL, *utf8; PurpleXferType xfer_type = purple_xfer_get_xfer_type(xfer); PurpleAccount *account = purple_xfer_get_account(xfer); PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); utf8 = g_filename_to_utf8(filename, -1, NULL, NULL, NULL); case PURPLE_XFER_TYPE_SEND: msg = g_strdup_printf(_("Error reading %s: \n%s.\n"), case PURPLE_XFER_TYPE_RECEIVE: msg = g_strdup_printf(_("Error writing %s: \n%s.\n"), msg = g_strdup_printf(_("Error accessing %s: \n%s.\n"), purple_xfer_conversation_write(xfer, msg, TRUE); purple_xfer_error(xfer_type, account, priv->who, msg); purple_xfer_choose_file_ok_cb(void *user_data, const char *filename) xfer = (PurpleXfer *)user_data; type = purple_xfer_get_xfer_type(xfer); if (g_stat(filename, &st) != 0) { if (type == PURPLE_XFER_TYPE_RECEIVE) { dir = g_path_get_dirname(filename); if (g_access(dir, mode) == 0) { purple_xfer_request_accepted(xfer, filename); NULL, PURPLE_NOTIFY_MSG_ERROR, NULL, _("Directory is not writable."), NULL, purple_request_cpar_from_account( purple_xfer_get_account(xfer)), (PurpleNotifyCloseCallback)purple_xfer_choose_file, xfer); purple_xfer_show_file_error(xfer, filename); purple_xfer_cancel_local(xfer); else if ((type == PURPLE_XFER_TYPE_SEND) && (st.st_size == 0)) { purple_notify_error(NULL, NULL, _("Cannot send a file of 0 bytes."), NULL, purple_request_cpar_from_account( purple_xfer_get_account(xfer))); purple_xfer_cancel_local(xfer); else if ((type == PURPLE_XFER_TYPE_SEND) && S_ISDIR(st.st_mode)) { * XXX - Sending a directory should be valid for some protocols. purple_notify_error(NULL, NULL, _("Cannot send a directory."), NULL, purple_request_cpar_from_account( purple_xfer_get_account(xfer))); purple_xfer_cancel_local(xfer); else if ((type == PURPLE_XFER_TYPE_RECEIVE) && S_ISDIR(st.st_mode)) { utf8 = g_filename_to_utf8(filename, -1, NULL, NULL, NULL); _("%s is not a regular file. Cowardly refusing to overwrite it.\n"), utf8); purple_notify_error(NULL, NULL, msg, NULL, purple_request_cpar_from_account( purple_xfer_get_account(xfer))); purple_xfer_request_denied(xfer); else if (type == PURPLE_XFER_TYPE_SEND) { if (g_access(filename, mode) == 0) { purple_xfer_request_accepted(xfer, filename); NULL, PURPLE_NOTIFY_MSG_ERROR, NULL, _("File is not readable."), NULL, purple_request_cpar_from_account( purple_xfer_get_account(xfer)), (PurpleNotifyCloseCallback)purple_xfer_choose_file, xfer); purple_xfer_request_accepted(xfer, filename); purple_xfer_choose_file_cancel_cb(void *user_data, const char *filename) PurpleXfer *xfer = (PurpleXfer *)user_data; purple_xfer_set_status(xfer, PURPLE_XFER_STATUS_CANCEL_LOCAL); if (purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_SEND) purple_xfer_cancel_local(xfer); purple_xfer_request_denied(xfer); purple_xfer_choose_file(PurpleXfer *xfer) purple_request_file(xfer, NULL, purple_xfer_get_filename(xfer), (purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_RECEIVE), G_CALLBACK(purple_xfer_choose_file_ok_cb), G_CALLBACK(purple_xfer_choose_file_cancel_cb), purple_request_cpar_from_account(purple_xfer_get_account(xfer)), cancel_recv_cb(PurpleXfer *xfer) purple_xfer_set_status(xfer, PURPLE_XFER_STATUS_CANCEL_LOCAL); purple_xfer_request_denied(xfer); purple_xfer_ask_recv(PurpleXfer *xfer) PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); /* If we have already accepted the request, ask the destination file if (purple_xfer_get_status(xfer) != PURPLE_XFER_STATUS_ACCEPTED) { PurpleRequestCommonParameters *cpar; PurpleBuddy *buddy = purple_blist_find_buddy(priv->account, priv->who); if (purple_xfer_get_filename(xfer) != NULL) size = purple_xfer_get_size(xfer); size_buf = purple_str_size_to_units(size); buf = g_strdup_printf(_("%s wants to send you %s (%s)"), buddy ? purple_buddy_get_alias(buddy) : priv->who, purple_xfer_get_filename(xfer), size_buf); buf = g_strdup_printf(_("%s wants to send you a file"), buddy ? purple_buddy_get_alias(buddy) : priv->who); if (priv->message != NULL) serv_got_im(purple_account_get_connection(priv->account), priv->who, priv->message, 0, time(NULL)); cpar = purple_request_cpar_from_account(priv->account); if ((thumb = purple_xfer_get_thumbnail(xfer, &thumb_size))) { purple_request_cpar_set_custom_icon(cpar, thumb, purple_request_accept_cancel(xfer, NULL, buf, NULL, PURPLE_DEFAULT_ACTION_NONE, cpar, xfer, G_CALLBACK(purple_xfer_choose_file), G_CALLBACK(cancel_recv_cb)); purple_xfer_choose_file(xfer); ask_accept_ok(PurpleXfer *xfer) purple_xfer_request_accepted(xfer, NULL); ask_accept_cancel(PurpleXfer *xfer) purple_xfer_request_denied(xfer); purple_xfer_ask_accept(PurpleXfer *xfer) PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); PurpleBuddy *buddy = purple_blist_find_buddy(priv->account, priv->who); buf = g_strdup_printf(_("Accept file transfer request from %s?"), buddy ? purple_buddy_get_alias(buddy) : priv->who); if (purple_xfer_get_remote_ip(xfer) && purple_xfer_get_remote_port(xfer)) buf2 = g_strdup_printf(_("A file is available for download from:\n" "Remote host: %s\nRemote port: %d"), purple_xfer_get_remote_ip(xfer), purple_xfer_get_remote_port(xfer)); purple_request_accept_cancel(xfer, NULL, buf, buf2, PURPLE_DEFAULT_ACTION_NONE, purple_request_cpar_from_account(priv->account), xfer, G_CALLBACK(ask_accept_ok), G_CALLBACK(ask_accept_cancel)); purple_xfer_request(PurpleXfer *xfer) PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); g_return_if_fail(priv != NULL); g_return_if_fail(priv->ops.init != NULL); if (purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_RECEIVE) purple_signal_emit(purple_xfers_get_handle(), "file-recv-request", xfer); if (purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_CANCEL_LOCAL) /* The file-transfer was cancelled by a plugin */ purple_xfer_cancel_local(xfer); else if (purple_xfer_get_filename(xfer) || purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_ACCEPTED) PurpleBuddy *buddy = purple_blist_find_buddy(priv->account, priv->who); message = g_strdup_printf(_("%s is offering to send file %s"), buddy ? purple_buddy_get_alias(buddy) : priv->who, purple_xfer_get_filename(xfer)); purple_xfer_conversation_write_with_thumbnail(xfer, message); /* Ask for a filename to save to if it's not already given by a plugin */ if (priv->local_filename == NULL) purple_xfer_ask_recv(xfer); purple_xfer_ask_accept(xfer); purple_xfer_choose_file(xfer); purple_xfer_request_accepted(PurpleXfer *xfer, const char *filename) PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); type = purple_xfer_get_xfer_type(xfer); account = purple_xfer_get_account(xfer); purple_debug_misc("xfer", "request accepted for %p\n", xfer); if (!filename && type == PURPLE_XFER_TYPE_RECEIVE) { purple_xfer_set_status(xfer, PURPLE_XFER_STATUS_ACCEPTED); buddy = purple_blist_find_buddy(account, priv->who); if (type == PURPLE_XFER_TYPE_SEND) { /* Check the filename. */ ui_ops = purple_xfer_get_ui_ops(xfer); if (g_strrstr(filename, "../") || g_strrstr(filename, "..\\")) if (g_strrstr(filename, "../")) utf8 = g_filename_to_utf8(filename, -1, NULL, NULL, NULL); msg = g_strdup_printf(_("%s is not a valid filename.\n"), utf8); purple_xfer_error(type, account, priv->who, msg); if (ui_ops == NULL || (ui_ops->ui_read == NULL && ui_ops->ui_write == NULL)) { if (g_stat(filename, &st) == -1) { purple_xfer_show_file_error(xfer, filename); purple_xfer_set_local_filename(xfer, filename); purple_xfer_set_size(xfer, st.st_size); purple_xfer_set_local_filename(xfer, filename); base = g_path_get_basename(filename); utf8 = g_filename_to_utf8(base, -1, NULL, NULL, NULL); purple_xfer_set_filename(xfer, utf8); msg = g_strdup_printf(_("Offering to send %s to %s"), utf8, buddy ? purple_buddy_get_alias(buddy) : priv->who); purple_xfer_conversation_write(xfer, msg, FALSE); purple_xfer_set_status(xfer, PURPLE_XFER_STATUS_ACCEPTED); purple_xfer_set_local_filename(xfer, filename); msg = g_strdup_printf(_("Starting transfer of %s from %s"), priv->filename, buddy ? purple_buddy_get_alias(buddy) : priv->who); purple_xfer_conversation_write(xfer, msg, FALSE); purple_xfer_request_denied(PurpleXfer *xfer) PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); g_return_if_fail(priv != NULL); purple_debug_misc("xfer", "xfer %p denied\n", xfer); if (priv->ops.request_denied != NULL) priv->ops.request_denied(xfer); int purple_xfer_get_fd(PurpleXfer *xfer) PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); g_return_val_if_fail(priv != NULL, 0); int purple_xfer_get_watcher(PurpleXfer *xfer) PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); g_return_val_if_fail(priv != NULL, 0); purple_xfer_get_xfer_type(const PurpleXfer *xfer) PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); g_return_val_if_fail(priv != NULL, PURPLE_XFER_TYPE_UNKNOWN); purple_xfer_get_account(const PurpleXfer *xfer) PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); g_return_val_if_fail(priv != NULL, NULL); purple_xfer_set_remote_user(PurpleXfer *xfer, const char *who) PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); g_return_if_fail(priv != NULL); priv->who = g_strdup(who); g_object_notify_by_pspec(G_OBJECT(xfer), properties[PROP_REMOTE_USER]); purple_xfer_get_remote_user(const PurpleXfer *xfer) PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); g_return_val_if_fail(priv != NULL, NULL); purple_xfer_get_status(const PurpleXfer *xfer) PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); g_return_val_if_fail(priv != NULL, PURPLE_XFER_STATUS_UNKNOWN); purple_xfer_is_cancelled(const PurpleXfer *xfer) g_return_val_if_fail(PURPLE_IS_XFER(xfer), TRUE); if ((purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_CANCEL_LOCAL) || (purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_CANCEL_REMOTE)) purple_xfer_is_completed(const PurpleXfer *xfer) g_return_val_if_fail(PURPLE_IS_XFER(xfer), TRUE); return (purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_DONE); purple_xfer_get_filename(const PurpleXfer *xfer) PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); g_return_val_if_fail(priv != NULL, NULL); purple_xfer_get_local_filename(const PurpleXfer *xfer) PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); g_return_val_if_fail(priv != NULL, NULL); return priv->local_filename; purple_xfer_get_bytes_sent(const PurpleXfer *xfer) PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); g_return_val_if_fail(priv != NULL, 0); purple_xfer_get_bytes_remaining(const PurpleXfer *xfer) PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); g_return_val_if_fail(priv != NULL, 0); return priv->bytes_remaining; purple_xfer_get_size(const PurpleXfer *xfer) PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); g_return_val_if_fail(priv != NULL, 0); purple_xfer_get_progress(const PurpleXfer *xfer) g_return_val_if_fail(PURPLE_IS_XFER(xfer), 0.0); if (purple_xfer_get_size(xfer) == 0) return ((double)purple_xfer_get_bytes_sent(xfer) / (double)purple_xfer_get_size(xfer)); purple_xfer_get_local_port(const PurpleXfer *xfer) PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); g_return_val_if_fail(priv != NULL, -1); purple_xfer_get_remote_ip(const PurpleXfer *xfer) PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); g_return_val_if_fail(priv != NULL, NULL); purple_xfer_get_remote_port(const PurpleXfer *xfer) PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); g_return_val_if_fail(priv != NULL, -1); return priv->remote_port; purple_xfer_get_start_time(const PurpleXfer *xfer) PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); g_return_val_if_fail(priv != NULL, 0); purple_xfer_get_end_time(const PurpleXfer *xfer) PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); g_return_val_if_fail(priv != NULL, 0); void purple_xfer_set_fd(PurpleXfer *xfer, int fd) PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); g_return_if_fail(priv != NULL); g_object_notify_by_pspec(G_OBJECT(xfer), properties[PROP_FD]); void purple_xfer_set_watcher(PurpleXfer *xfer, int watcher) PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); g_return_if_fail(priv != NULL); g_object_notify_by_pspec(G_OBJECT(xfer), properties[PROP_WATCHER]); purple_xfer_set_completed(PurpleXfer *xfer, gboolean completed) PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); g_return_if_fail(priv != NULL); PurpleIMConversation *im; purple_xfer_set_status(xfer, PURPLE_XFER_STATUS_DONE); if (purple_xfer_get_filename(xfer) != NULL) char *filename = g_markup_escape_text(purple_xfer_get_filename(xfer), -1); if (purple_xfer_get_local_filename(xfer) && purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_RECEIVE) char *local = g_markup_escape_text(purple_xfer_get_local_filename(xfer), -1); msg = g_strdup_printf(_("Transfer of file <A HREF=\"file://%s\">%s</A> complete"), msg = g_strdup_printf(_("Transfer of file %s complete"), msg = g_strdup(_("File transfer complete")); im = purple_conversations_find_im_with_account(priv->who, purple_xfer_get_account(xfer)); purple_conversation_write(PURPLE_CONVERSATION(im), NULL, msg, PURPLE_MESSAGE_SYSTEM, time(NULL)); ui_ops = purple_xfer_get_ui_ops(xfer); if (ui_ops != NULL && ui_ops->update_progress != NULL) ui_ops->update_progress(xfer, purple_xfer_get_progress(xfer)); purple_xfer_set_message(PurpleXfer *xfer, const char *message) PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); g_return_if_fail(priv != NULL); priv->message = g_strdup(message); g_object_notify_by_pspec(G_OBJECT(xfer), properties[PROP_MESSAGE]); purple_xfer_get_message(const PurpleXfer *xfer) PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); g_return_val_if_fail(priv != NULL, NULL); purple_xfer_set_filename(PurpleXfer *xfer, const char *filename) PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); g_return_if_fail(priv != NULL); priv->filename = g_strdup(filename); g_object_notify_by_pspec(G_OBJECT(xfer), properties[PROP_FILENAME]); purple_xfer_set_local_filename(PurpleXfer *xfer, const char *filename) PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); g_return_if_fail(priv != NULL); g_free(priv->local_filename); priv->local_filename = g_strdup(filename); g_object_notify_by_pspec(G_OBJECT(xfer), properties[PROP_LOCAL_FILENAME]); purple_xfer_set_size(PurpleXfer *xfer, goffset size) PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); g_return_if_fail(priv != NULL); priv->bytes_remaining = priv->size - purple_xfer_get_bytes_sent(xfer); g_object_notify_by_pspec(G_OBJECT(xfer), properties[PROP_FILE_SIZE]); purple_xfer_set_local_port(PurpleXfer *xfer, guint16 local_port) PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); g_return_if_fail(priv != NULL); priv->local_port = local_port; g_object_notify_by_pspec(G_OBJECT(xfer), properties[PROP_LOCAL_PORT]); purple_xfer_set_bytes_sent(PurpleXfer *xfer, goffset bytes_sent) PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); g_return_if_fail(priv != NULL); priv->bytes_sent = bytes_sent; if (purple_xfer_get_size(xfer) > 0) priv->bytes_remaining = purple_xfer_get_size(xfer) - bytes_sent; g_object_notify_by_pspec(G_OBJECT(xfer), properties[PROP_BYTES_SENT]); purple_xfer_get_ui_ops(const PurpleXfer *xfer) PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); g_return_val_if_fail(priv != NULL, NULL); purple_xfer_set_init_fnc(PurpleXfer *xfer, void (*fnc)(PurpleXfer *)) PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); g_return_if_fail(priv != NULL); void purple_xfer_set_request_denied_fnc(PurpleXfer *xfer, void (*fnc)(PurpleXfer *)) PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); g_return_if_fail(priv != NULL); priv->ops.request_denied = fnc; purple_xfer_set_read_fnc(PurpleXfer *xfer, gssize (*fnc)(guchar **, size_t, PurpleXfer *)) PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); g_return_if_fail(priv != NULL); purple_xfer_set_write_fnc(PurpleXfer *xfer, gssize (*fnc)(const guchar *, size_t, PurpleXfer *)) PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); g_return_if_fail(priv != NULL); purple_xfer_set_ack_fnc(PurpleXfer *xfer, void (*fnc)(PurpleXfer *, const guchar *, size_t)) PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); g_return_if_fail(priv != NULL); purple_xfer_set_start_fnc(PurpleXfer *xfer, void (*fnc)(PurpleXfer *)) PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); g_return_if_fail(priv != NULL); purple_xfer_set_end_fnc(PurpleXfer *xfer, void (*fnc)(PurpleXfer *)) PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); g_return_if_fail(priv != NULL); purple_xfer_set_cancel_send_fnc(PurpleXfer *xfer, void (*fnc)(PurpleXfer *)) PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); g_return_if_fail(priv != NULL); priv->ops.cancel_send = fnc; purple_xfer_set_cancel_recv_fnc(PurpleXfer *xfer, void (*fnc)(PurpleXfer *)) PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); g_return_if_fail(priv != NULL); priv->ops.cancel_recv = fnc; purple_xfer_increase_buffer_size(PurpleXfer *xfer) PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); priv->current_buffer_size = MIN(priv->current_buffer_size * 1.5, purple_xfer_read(PurpleXfer *xfer, guchar **buffer) PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); g_return_val_if_fail(priv != NULL, 0); g_return_val_if_fail(buffer != NULL, 0); if (purple_xfer_get_size(xfer) == 0) s = priv->current_buffer_size; s = MIN((gssize)purple_xfer_get_bytes_remaining(xfer), (gssize)priv->current_buffer_size); if (priv->ops.read != NULL) { r = (priv->ops.read)(buffer, s, xfer); r = read(priv->fd, *buffer, s); if (r < 0 && errno == EAGAIN) if (r >= 0 && (gsize)r == priv->current_buffer_size) * We managed to read the entire buffer. This means our this * network is fast and our buffer is too small, so make it purple_xfer_increase_buffer_size(xfer); purple_xfer_write(PurpleXfer *xfer, const guchar *buffer, gsize size) PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); g_return_val_if_fail(priv != NULL, 0); g_return_val_if_fail(buffer != NULL, 0); g_return_val_if_fail(size != 0, 0); s = MIN((gssize)purple_xfer_get_bytes_remaining(xfer), (gssize)size); if (priv->ops.write != NULL) { r = (priv->ops.write)(buffer, s, xfer); r = write(priv->fd, buffer, s); if (r < 0 && errno == EAGAIN) if (r >= 0 && (purple_xfer_get_bytes_sent(xfer)+r) >= purple_xfer_get_size(xfer) && !purple_xfer_is_completed(xfer)) purple_xfer_set_completed(xfer, TRUE); purple_xfer_write_file(PurpleXfer *xfer, const guchar *buffer, gsize size) PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); g_return_val_if_fail(priv != NULL, FALSE); g_return_val_if_fail(buffer != NULL, FALSE); ui_ops = purple_xfer_get_ui_ops(xfer); fs_known = (purple_xfer_get_size(xfer) > 0); if (fs_known && (goffset)size > purple_xfer_get_bytes_remaining(xfer)) { purple_debug_warning("xfer", "Got too much data (truncating at %" G_GOFFSET_FORMAT ").\n", purple_xfer_get_size(xfer)); size = purple_xfer_get_bytes_remaining(xfer); if (ui_ops && ui_ops->ui_write) wc = ui_ops->ui_write(xfer, buffer, size); if (priv->dest_fp == NULL) { purple_debug_error("xfer", "File is not opened for writing\n"); purple_xfer_cancel_local(xfer); wc = fwrite(buffer, size, 1, priv->dest_fp); purple_debug_error("xfer", "Unable to write whole buffer.\n"); purple_xfer_cancel_local(xfer); purple_xfer_set_bytes_sent(xfer, purple_xfer_get_bytes_sent(xfer) + purple_xfer_read_file(PurpleXfer *xfer, guchar *buffer, gsize size) PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); g_return_val_if_fail(priv != NULL, FALSE); g_return_val_if_fail(buffer != NULL, FALSE); ui_ops = purple_xfer_get_ui_ops(xfer); if (ui_ops && ui_ops->ui_read) { guchar *buffer_got = NULL; got_len = ui_ops->ui_read(xfer, &buffer_got, size); if (got_len >= 0 && (gsize)got_len > size) { purple_debug_error("xfer", "Got too much data from UI.\n"); purple_xfer_cancel_local(xfer); memcpy(buffer, buffer_got, got_len); if (priv->dest_fp == NULL) { purple_debug_error("xfer", "File is not opened for reading\n"); purple_xfer_cancel_local(xfer); got_len = fread(buffer, size, 1, priv->dest_fp); if ((got_len < 0 || (gsize)got_len != size) && purple_debug_error("xfer", "Unable to read file.\n"); purple_xfer_cancel_local(xfer); purple_xfer_set_bytes_sent(xfer, purple_xfer_get_bytes_sent(xfer) + got_len); do_transfer(PurpleXfer *xfer) PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); ui_ops = purple_xfer_get_ui_ops(xfer); if (priv->type == PURPLE_XFER_TYPE_RECEIVE) { r = purple_xfer_read(xfer, &buffer); if (!purple_xfer_write_file(xfer, buffer, r)) { if ((purple_xfer_get_size(xfer) > 0) && ((purple_xfer_get_bytes_sent(xfer)+r) >= purple_xfer_get_size(xfer))) purple_xfer_set_completed(xfer, TRUE); purple_xfer_cancel_remote(xfer); } else if (priv->type == PURPLE_XFER_TYPE_SEND) { gsize s = MIN((gsize)purple_xfer_get_bytes_remaining(xfer), (gsize)priv->current_buffer_size); /* this is so the prpl can keep the connection open if it needs to for some odd reason. */ purple_input_remove(priv->watcher); purple_xfer_set_watcher(xfer, 0); if (priv->buffer->len < s) { buffer = g_new(guchar, s); result = purple_xfer_read_file(xfer, buffer, s); * The UI claimed it was ready, but didn't have any data for * us... It will call purple_xfer_ui_ready when ready, which * sets back up this watcher. if (priv->watcher != 0) { purple_input_remove(priv->watcher); purple_xfer_set_watcher(xfer, 0); /* Need to indicate the prpl is still ready... */ priv->ready |= PURPLE_XFER_READY_PRPL; g_byte_array_append(priv->buffer, buffer, result); buffer = priv->buffer->data; result = priv->buffer->len; r = purple_xfer_write(xfer, buffer, result); purple_xfer_cancel_remote(xfer); /* We don't free buffer if priv->buffer is set, because in that case buffer doesn't belong to us. */ } else if (r == result) { * We managed to write the entire buffer. This means our * network is fast and our buffer is too small, so make it purple_xfer_increase_buffer_size(xfer); if (ui_ops && ui_ops->data_not_sent) ui_ops->data_not_sent(xfer, buffer + r, result - r); * If we wrote the whole buffer the byte array will be empty * Otherwise we'll keep what wasn't sent for next time. g_byte_array_remove_range(priv->buffer, 0, r); purple_xfer_set_bytes_sent(xfer, priv->bytes_sent + r); if (priv->ops.ack != NULL) priv->ops.ack(xfer, buffer, r); if (ui_ops != NULL && ui_ops->update_progress != NULL) ui_ops->update_progress(xfer, purple_xfer_get_progress(xfer)); if (purple_xfer_is_completed(xfer)) transfer_cb(gpointer data, gint source, PurpleInputCondition condition) PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); if (priv->dest_fp == NULL) { /* The UI is moderating its side manually */ if (0 == (priv->ready & PURPLE_XFER_READY_UI)) { priv->ready |= PURPLE_XFER_READY_PRPL; purple_input_remove(priv->watcher); purple_xfer_set_watcher(xfer, 0); purple_debug_misc("xfer", "prpl is ready on ft %p, waiting for UI\n", xfer); priv->ready = PURPLE_XFER_READY_NONE; begin_transfer(PurpleXfer *xfer, PurpleInputCondition cond) PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); PurpleXferType type = purple_xfer_get_xfer_type(xfer); PurpleXferUiOps *ui_ops = purple_xfer_get_ui_ops(xfer); if (priv->start_time != 0) { purple_debug_error("xfer", "Transfer is being started multiple times\n"); if (ui_ops == NULL || (ui_ops->ui_read == NULL && ui_ops->ui_write == NULL)) { priv->dest_fp = g_fopen(purple_xfer_get_local_filename(xfer), type == PURPLE_XFER_TYPE_RECEIVE ? "wb" : "rb"); if (priv->dest_fp == NULL) { purple_xfer_show_file_error(xfer, purple_xfer_get_local_filename(xfer)); purple_xfer_cancel_local(xfer); fseek(priv->dest_fp, priv->bytes_sent, SEEK_SET); purple_xfer_set_watcher(xfer, purple_input_add(priv->fd, cond, transfer_cb, xfer)); priv->start_time = time(NULL); g_object_notify_by_pspec(G_OBJECT(xfer), properties[PROP_START_TIME]); if (priv->ops.start != NULL) connect_cb(gpointer data, gint source, const gchar *error_message) PurpleXfer *xfer = PURPLE_XFER(data); purple_xfer_cancel_local(xfer); purple_xfer_set_fd(xfer, source); begin_transfer(xfer, PURPLE_INPUT_READ); purple_xfer_ui_ready(PurpleXfer *xfer) PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); PurpleInputCondition cond; g_return_if_fail(priv != NULL); priv->ready |= PURPLE_XFER_READY_UI; if (0 == (priv->ready & PURPLE_XFER_READY_PRPL)) { purple_debug_misc("xfer", "UI is ready on ft %p, waiting for prpl\n", xfer); purple_debug_misc("xfer", "UI (and prpl) ready on ft %p, so proceeding\n", xfer); type = purple_xfer_get_xfer_type(xfer); if (type == PURPLE_XFER_TYPE_SEND) cond = PURPLE_INPUT_WRITE; else /* if (type == PURPLE_XFER_TYPE_RECEIVE) */ cond = PURPLE_INPUT_READ; if (priv->watcher == 0 && priv->fd != -1) purple_xfer_set_watcher(xfer, purple_input_add(priv->fd, cond, transfer_cb, xfer)); priv->ready = PURPLE_XFER_READY_NONE; purple_xfer_prpl_ready(PurpleXfer *xfer) PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); g_return_if_fail(priv != NULL); priv->ready |= PURPLE_XFER_READY_PRPL; /* I don't think fwrite/fread are ever *not* ready */ if (priv->dest_fp == NULL && 0 == (priv->ready & PURPLE_XFER_READY_UI)) { purple_debug_misc("xfer", "prpl is ready on ft %p, waiting for UI\n", xfer); purple_debug_misc("xfer", "Prpl (and UI) ready on ft %p, so proceeding\n", xfer); priv->ready = PURPLE_XFER_READY_NONE; purple_xfer_start(PurpleXfer *xfer, int fd, const char *ip, guint16 port) PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); PurpleInputCondition cond; g_return_if_fail(priv != NULL); g_return_if_fail(purple_xfer_get_xfer_type(xfer) != PURPLE_XFER_TYPE_UNKNOWN); type = purple_xfer_get_xfer_type(xfer); purple_xfer_set_status(xfer, PURPLE_XFER_STATUS_STARTED); if (type == PURPLE_XFER_TYPE_RECEIVE) { cond = PURPLE_INPUT_READ; priv->remote_ip = g_strdup(ip); priv->remote_port = port; g_object_freeze_notify(obj); g_object_notify_by_pspec(obj, properties[PROP_REMOTE_IP]); g_object_notify_by_pspec(obj, properties[PROP_REMOTE_PORT]); g_object_thaw_notify(obj); /* Establish a file descriptor. */ purple_proxy_connect(NULL, priv->account, priv->remote_ip, priv->remote_port, connect_cb, xfer); purple_xfer_set_fd(xfer, fd); cond = PURPLE_INPUT_WRITE; purple_xfer_set_fd(xfer, fd); begin_transfer(xfer, cond); purple_xfer_end(PurpleXfer *xfer) PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); g_return_if_fail(priv != NULL); /* See if we are actually trying to cancel this. */ if (!purple_xfer_is_completed(xfer)) { purple_xfer_cancel_local(xfer); priv->end_time = time(NULL); g_object_notify_by_pspec(G_OBJECT(xfer), properties[PROP_END_TIME]); if (priv->ops.end != NULL) if (priv->watcher != 0) { purple_input_remove(priv->watcher); purple_xfer_set_watcher(xfer, 0); if (priv->dest_fp != NULL) { purple_xfer_add(PurpleXfer *xfer) g_return_if_fail(PURPLE_IS_XFER(xfer)); ui_ops = purple_xfer_get_ui_ops(xfer); if (ui_ops != NULL && ui_ops->add_xfer != NULL) purple_xfer_cancel_local(PurpleXfer *xfer) PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); g_return_if_fail(priv != NULL); /* TODO: We definitely want to close any open request dialogs associated with this transfer. However, in some cases the request dialog might own a reference on the xfer. This happens at least with the "%s wants to send you %s" dialog from purple_xfer_ask_recv(). In these cases the ref count will not be decremented when the request dialog is closed, so the ref count will never reach 0 and the xfer will never be freed. This is a memleak and should be fixed. It's not clear what the correct fix is. Probably requests should have a destroy function that is called when the request is destroyed. But also, ref counting xfer objects makes this code REALLY complicated. An alternate fix is to not ref count and instead just make sure the object still exists when we try to use it. */ purple_request_close_with_handle(xfer); purple_xfer_set_status(xfer, PURPLE_XFER_STATUS_CANCEL_LOCAL); priv->end_time = time(NULL); g_object_notify_by_pspec(G_OBJECT(xfer), properties[PROP_END_TIME]); if (purple_xfer_get_filename(xfer) != NULL) msg = g_strdup_printf(_("You cancelled the transfer of %s"), purple_xfer_get_filename(xfer)); msg = g_strdup(_("File transfer cancelled")); purple_xfer_conversation_write(xfer, msg, FALSE); if (purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_SEND) if (priv->ops.cancel_send != NULL) priv->ops.cancel_send(xfer); if (priv->ops.cancel_recv != NULL) priv->ops.cancel_recv(xfer); if (priv->watcher != 0) { purple_input_remove(priv->watcher); purple_xfer_set_watcher(xfer, 0); if (priv->dest_fp != NULL) { ui_ops = purple_xfer_get_ui_ops(xfer); if (ui_ops != NULL && ui_ops->cancel_local != NULL) ui_ops->cancel_local(xfer); priv->bytes_remaining = 0; purple_xfer_cancel_remote(PurpleXfer *xfer) PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); g_return_if_fail(priv != NULL); purple_request_close_with_handle(xfer); purple_xfer_set_status(xfer, PURPLE_XFER_STATUS_CANCEL_REMOTE); priv->end_time = time(NULL); g_object_notify_by_pspec(G_OBJECT(xfer), properties[PROP_END_TIME]); account = purple_xfer_get_account(xfer); buddy = purple_blist_find_buddy(account, priv->who); if (purple_xfer_get_filename(xfer) != NULL) msg = g_strdup_printf(_("%s cancelled the transfer of %s"), buddy ? purple_buddy_get_alias(buddy) : priv->who, purple_xfer_get_filename(xfer)); msg = g_strdup_printf(_("%s cancelled the file transfer"), buddy ? purple_buddy_get_alias(buddy) : priv->who); purple_xfer_conversation_write(xfer, msg, TRUE); purple_xfer_error(purple_xfer_get_xfer_type(xfer), account, priv->who, msg); if (purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_SEND) if (priv->ops.cancel_send != NULL) priv->ops.cancel_send(xfer); if (priv->ops.cancel_recv != NULL) priv->ops.cancel_recv(xfer); if (priv->watcher != 0) { purple_input_remove(priv->watcher); purple_xfer_set_watcher(xfer, 0); if (priv->dest_fp != NULL) { ui_ops = purple_xfer_get_ui_ops(xfer); if (ui_ops != NULL && ui_ops->cancel_remote != NULL) ui_ops->cancel_remote(xfer); priv->bytes_remaining = 0; purple_xfer_error(PurpleXferType type, PurpleAccount *account, const char *who, const char *msg) g_return_if_fail(msg != NULL); g_return_if_fail(type != PURPLE_XFER_TYPE_UNKNOWN); buddy = purple_blist_find_buddy(account, who); who = purple_buddy_get_alias(buddy); if (type == PURPLE_XFER_TYPE_SEND) title = g_strdup_printf(_("File transfer to %s failed."), who); title = g_strdup_printf(_("File transfer from %s failed."), who); purple_notify_error(NULL, NULL, title, msg, purple_request_cpar_from_account(account)); purple_xfer_update_progress(PurpleXfer *xfer) g_return_if_fail(PURPLE_IS_XFER(xfer)); ui_ops = purple_xfer_get_ui_ops(xfer); if (ui_ops != NULL && ui_ops->update_progress != NULL) ui_ops->update_progress(xfer, purple_xfer_get_progress(xfer)); purple_xfer_get_thumbnail(const PurpleXfer *xfer, gsize *len) PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); g_return_val_if_fail(priv != NULL, NULL); *len = priv->thumbnail_size; return priv->thumbnail_data; purple_xfer_get_thumbnail_mimetype(const PurpleXfer *xfer) PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); g_return_val_if_fail(priv != NULL, NULL); return priv->thumbnail_mimetype; purple_xfer_set_thumbnail(PurpleXfer *xfer, gconstpointer thumbnail, gsize size, const gchar *mimetype) PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); g_return_if_fail(priv != NULL); g_free(priv->thumbnail_data); g_free(priv->thumbnail_mimetype); if (thumbnail && size > 0) { priv->thumbnail_data = g_memdup(thumbnail, size); priv->thumbnail_size = size; priv->thumbnail_mimetype = g_strdup(mimetype); priv->thumbnail_data = NULL; priv->thumbnail_size = 0; priv->thumbnail_mimetype = NULL; purple_xfer_prepare_thumbnail(PurpleXfer *xfer, const gchar *formats) PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); g_return_if_fail(priv != NULL); if (priv->ui_ops->add_thumbnail) { priv->ui_ops->add_thumbnail(xfer, formats); purple_xfer_set_protocol_data(PurpleXfer *xfer, gpointer proto_data) PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); g_return_if_fail(priv != NULL); priv->proto_data = proto_data; purple_xfer_get_protocol_data(const PurpleXfer *xfer) PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); g_return_val_if_fail(priv != NULL, NULL); void purple_xfer_set_ui_data(PurpleXfer *xfer, gpointer ui_data) g_return_if_fail(PURPLE_IS_XFER(xfer)); gpointer purple_xfer_get_ui_data(const PurpleXfer *xfer) g_return_val_if_fail(PURPLE_IS_XFER(xfer), NULL); /************************************************************************** **************************************************************************/ /* Set method for GObject properties */ purple_xfer_set_property(GObject *obj, guint param_id, const GValue *value, PurpleXfer *xfer = PURPLE_XFER(obj); PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); priv->type = g_value_get_enum(value); priv->account = g_value_get_object(value); purple_xfer_set_remote_user(xfer, g_value_get_string(value)); purple_xfer_set_message(xfer, g_value_get_string(value)); purple_xfer_set_filename(xfer, g_value_get_string(value)); case PROP_LOCAL_FILENAME: purple_xfer_set_local_filename(xfer, g_value_get_string(value)); purple_xfer_set_size(xfer, g_value_get_int64(value)); purple_xfer_set_local_port(xfer, g_value_get_int(value)); purple_xfer_set_fd(xfer, g_value_get_int(value)); purple_xfer_set_watcher(xfer, g_value_get_int(value)); purple_xfer_set_bytes_sent(xfer, g_value_get_int64(value)); purple_xfer_set_status(xfer, g_value_get_enum(value)); G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec); /* Get method for GObject properties */ purple_xfer_get_property(GObject *obj, guint param_id, GValue *value, PurpleXfer *xfer = PURPLE_XFER(obj); g_value_set_enum(value, purple_xfer_get_xfer_type(xfer)); g_value_set_object(value, purple_xfer_get_account(xfer)); g_value_set_string(value, purple_xfer_get_remote_user(xfer)); g_value_set_string(value, purple_xfer_get_message(xfer)); g_value_set_string(value, purple_xfer_get_filename(xfer)); case PROP_LOCAL_FILENAME: g_value_set_string(value, purple_xfer_get_local_filename(xfer)); g_value_set_int64(value, purple_xfer_get_size(xfer)); g_value_set_string(value, purple_xfer_get_remote_ip(xfer)); g_value_set_int(value, purple_xfer_get_local_port(xfer)); g_value_set_int(value, purple_xfer_get_remote_port(xfer)); g_value_set_int(value, purple_xfer_get_fd(xfer)); g_value_set_int(value, purple_xfer_get_watcher(xfer)); g_value_set_int64(value, purple_xfer_get_bytes_sent(xfer)); g_value_set_int(value, purple_xfer_get_start_time(xfer)); g_value_set_int64(value, purple_xfer_get_start_time(xfer)); #error Unknown size of time_t g_value_set_int(value, purple_xfer_get_end_time(xfer)); g_value_set_int64(value, purple_xfer_get_end_time(xfer)); #error Unknown size of time_t g_value_set_enum(value, purple_xfer_get_status(xfer)); G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec); /* GObject initialization function */ purple_xfer_init(GTypeInstance *instance, gpointer klass) PurpleXfer *xfer = PURPLE_XFER(instance); PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); PURPLE_DBUS_REGISTER_POINTER(xfer, PurpleXfer); priv->ui_ops = purple_xfers_get_ui_ops(); priv->current_buffer_size = FT_INITIAL_BUFFER_SIZE; priv->ready = PURPLE_XFER_READY_NONE; /* Called when done constructing */ purple_xfer_constructed(GObject *object) PurpleXfer *xfer = PURPLE_XFER(object); PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); parent_class->constructed(object); ui_ops = purple_xfers_get_ui_ops(); if (ui_ops && ui_ops->data_not_sent) { /* If the ui will handle unsent data no need for buffer */ priv->buffer = g_byte_array_sized_new(FT_INITIAL_BUFFER_SIZE); if (ui_ops != NULL && ui_ops->new_xfer != NULL) xfers = g_list_prepend(xfers, xfer); /* GObject finalize function */ purple_xfer_finalize(GObject *object) PurpleXfer *xfer = PURPLE_XFER(object); PurpleXferPrivate *priv = PURPLE_XFER_GET_PRIVATE(xfer); /* Close the file browser, if it's open */ purple_request_close_with_handle(xfer); if (purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_STARTED) purple_xfer_cancel_local(xfer); ui_ops = purple_xfer_get_ui_ops(xfer); if (ui_ops != NULL && ui_ops->destroy != NULL) xfers = g_list_remove(xfers, xfer); g_free(priv->local_filename); g_byte_array_free(priv->buffer, TRUE); g_free(priv->thumbnail_data); g_free(priv->thumbnail_mimetype); PURPLE_DBUS_UNREGISTER_POINTER(xfer); parent_class->finalize(object); /* Class initializer function */ purple_xfer_class_init(PurpleXferClass *klass) GObjectClass *obj_class = G_OBJECT_CLASS(klass); parent_class = g_type_class_peek_parent(klass); obj_class->finalize = purple_xfer_finalize; obj_class->constructed = purple_xfer_constructed; obj_class->get_property = purple_xfer_get_property; obj_class->set_property = purple_xfer_set_property; g_type_class_add_private(klass, sizeof(PurpleXferPrivate)); properties[PROP_TYPE] = g_param_spec_enum("type", "Transfer type", "The type of file transfer.", PURPLE_TYPE_XFER_TYPE, PURPLE_XFER_TYPE_UNKNOWN, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | properties[PROP_ACCOUNT] = g_param_spec_object("account", "Account", "The account sending or receiving the file.", G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | properties[PROP_REMOTE_USER] = g_param_spec_string("remote-user", "The name of the remote user.", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS); properties[PROP_MESSAGE] = g_param_spec_string("message", "Message", "The message for the file transfer.", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); properties[PROP_FILENAME] = g_param_spec_string("filename", "Filename", "The filename for the file transfer.", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); properties[PROP_LOCAL_FILENAME] = g_param_spec_string("local-filename", "The local filename for the file transfer.", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); properties[PROP_FILE_SIZE] = g_param_spec_int64("file-size", "File size", "Size of the file in a file transfer.", G_MININT64, G_MAXINT64, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); properties[PROP_REMOTE_IP] = g_param_spec_string("remote-ip", "Remote IP", "The remote IP address in the file transfer.", NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); properties[PROP_LOCAL_PORT] = g_param_spec_int("local-port", "Local port", "The local port number in the file transfer.", G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); properties[PROP_REMOTE_PORT] = g_param_spec_int("remote-port", "The remote port number in the file transfer.", G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); properties[PROP_FD] = g_param_spec_int("fd", "Socket FD", "The socket file descriptor.", G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); properties[PROP_WATCHER] = g_param_spec_int("watcher", "Watcher", "The watcher for the file transfer.", G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); properties[PROP_BYTES_SENT] = g_param_spec_int64("bytes-sent", "Bytes sent", "The number of bytes sent (or received) so far.", G_MININT64, G_MAXINT64, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); properties[PROP_START_TIME] = #error Unknown size of time_t ("start-time", "Start time", "The time the transfer of a file started.", G_MININT64, G_MAXINT64, 0, #error Unknown size of time_t G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); properties[PROP_END_TIME] = #error Unknown size of time_t "The time the transfer of a file ended.", G_MININT64, G_MAXINT64, 0, #error Unknown size of time_t G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); properties[PROP_STATUS] = g_param_spec_enum("status", "Status", "The current status for the file transfer.", PURPLE_TYPE_XFER_STATUS, PURPLE_XFER_STATUS_UNKNOWN, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_properties(obj_class, PROP_LAST, properties); purple_xfer_get_type(void) static const GTypeInfo info = { (GClassInitFunc)purple_xfer_class_init, (GInstanceInitFunc)purple_xfer_init, type = g_type_register_static(G_TYPE_OBJECT, "PurpleXfer", purple_xfer_new(PurpleAccount *account, PurpleXferType type, const char *who) g_return_val_if_fail(type != PURPLE_XFER_TYPE_UNKNOWN, NULL); g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL); g_return_val_if_fail(who != NULL, NULL); return g_object_new(PURPLE_TYPE_XFER, /************************************************************************** * File Transfer Subsystem API **************************************************************************/ purple_xfers_get_handle(void) { purple_xfers_init(void) { void *handle = purple_xfers_get_handle(); purple_signal_register(handle, "file-recv-accept", purple_marshal_VOID__POINTER, G_TYPE_NONE, 1, purple_signal_register(handle, "file-send-accept", purple_marshal_VOID__POINTER, G_TYPE_NONE, 1, purple_signal_register(handle, "file-recv-start", purple_marshal_VOID__POINTER, G_TYPE_NONE, 1, purple_signal_register(handle, "file-send-start", purple_marshal_VOID__POINTER, G_TYPE_NONE, 1, purple_signal_register(handle, "file-send-cancel", purple_marshal_VOID__POINTER, G_TYPE_NONE, 1, purple_signal_register(handle, "file-recv-cancel", purple_marshal_VOID__POINTER, G_TYPE_NONE, 1, purple_signal_register(handle, "file-send-complete", purple_marshal_VOID__POINTER, G_TYPE_NONE, 1, purple_signal_register(handle, "file-recv-complete", purple_marshal_VOID__POINTER, G_TYPE_NONE, 1, purple_signal_register(handle, "file-recv-request", purple_marshal_VOID__POINTER, G_TYPE_NONE, 1, purple_xfers_uninit(void) void *handle = purple_xfers_get_handle(); purple_signals_disconnect_by_handle(handle); purple_signals_unregister_by_instance(handle); purple_xfers_set_ui_ops(PurpleXferUiOps *ops) { purple_xfers_get_ui_ops(void) {