* @file ft.c File Transfer API * Gaim 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA static GaimXferUiOps *xfer_ui_ops = NULL; gaim_xfer_new(GaimAccount *account, GaimXferType type, const char *who) g_return_val_if_fail(type != GAIM_XFER_UNKNOWN, NULL); g_return_val_if_fail(account != NULL, NULL); g_return_val_if_fail(who != NULL, NULL); xfer = g_new0(GaimXfer, 1); xfer->who = g_strdup(who); xfer->ui_ops = gaim_xfers_get_ui_ops(); ui_ops = gaim_xfer_get_ui_ops(xfer); if (ui_ops != NULL && ui_ops->new_xfer != NULL) gaim_xfer_destroy(GaimXfer *xfer) g_return_if_fail(xfer != NULL); /* Close the file browser, if it's open */ gaim_request_close_with_handle(xfer); if (gaim_xfer_get_status(xfer) == GAIM_XFER_STATUS_STARTED) gaim_xfer_cancel_local(xfer); ui_ops = gaim_xfer_get_ui_ops(xfer); if (ui_ops != NULL && ui_ops->destroy != NULL) g_free(xfer->local_filename); gaim_xfer_ref(GaimXfer *xfer) g_return_if_fail(xfer != NULL); gaim_xfer_unref(GaimXfer *xfer) g_return_if_fail(xfer != NULL); g_return_if_fail(xfer->ref > 0); gaim_xfer_set_status(GaimXfer *xfer, GaimXferStatusType status) g_return_if_fail(xfer != NULL); if(xfer->type == GAIM_XFER_SEND) { case GAIM_XFER_STATUS_ACCEPTED: gaim_signal_emit(gaim_xfers_get_handle(), "file-send-accept", xfer); case GAIM_XFER_STATUS_STARTED: gaim_signal_emit(gaim_xfers_get_handle(), "file-send-start", xfer); case GAIM_XFER_STATUS_DONE: gaim_signal_emit(gaim_xfers_get_handle(), "file-send-complete", xfer); case GAIM_XFER_STATUS_CANCEL_LOCAL: case GAIM_XFER_STATUS_CANCEL_REMOTE: gaim_signal_emit(gaim_xfers_get_handle(), "file-send-cancel", xfer); } else if(xfer->type == GAIM_XFER_RECEIVE) { case GAIM_XFER_STATUS_ACCEPTED: gaim_signal_emit(gaim_xfers_get_handle(), "file-recv-accept", xfer); case GAIM_XFER_STATUS_STARTED: gaim_signal_emit(gaim_xfers_get_handle(), "file-recv-start", xfer); case GAIM_XFER_STATUS_DONE: gaim_signal_emit(gaim_xfers_get_handle(), "file-recv-complete", xfer); case GAIM_XFER_STATUS_CANCEL_LOCAL: case GAIM_XFER_STATUS_CANCEL_REMOTE: gaim_signal_emit(gaim_xfers_get_handle(), "file-recv-cancel", xfer); void gaim_xfer_conversation_write(GaimXfer *xfer, char *message, gboolean is_error) GaimConversation *conv = NULL; GaimMessageFlags flags = GAIM_MESSAGE_SYSTEM; g_return_if_fail(xfer != NULL); g_return_if_fail(message != NULL); conv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_IM, xfer->who, gaim_xfer_get_account(xfer)); escaped = g_markup_escape_text(message, -1); flags = GAIM_MESSAGE_ERROR; gaim_conversation_write(conv, NULL, escaped, flags, time(NULL)); static void gaim_xfer_show_file_error(GaimXfer *xfer, const char *filename) gchar *msg = NULL, *utf8; GaimXferType xfer_type = gaim_xfer_get_type(xfer); GaimAccount *account = gaim_xfer_get_account(xfer); utf8 = g_filename_to_utf8(filename, -1, NULL, NULL, NULL); msg = g_strdup_printf(_("Error reading %s: \n%s.\n"), msg = g_strdup_printf(_("Error writing %s: \n%s.\n"), msg = g_strdup_printf(_("Error accessing %s: \n%s.\n"), gaim_xfer_conversation_write(xfer, msg, TRUE); gaim_xfer_error(xfer_type, account, xfer->who, msg); gaim_xfer_choose_file_ok_cb(void *user_data, const char *filename) xfer = (GaimXfer *)user_data; if (g_stat(filename, &st) != 0) { if (gaim_xfer_get_type(xfer) == GAIM_XFER_RECEIVE) { gaim_xfer_request_accepted(xfer, filename); gaim_xfer_show_file_error(xfer, filename); gaim_xfer_request_denied(xfer); else if ((gaim_xfer_get_type(xfer) == GAIM_XFER_SEND) && gaim_notify_error(NULL, NULL, _("Cannot send a file of 0 bytes."), NULL); gaim_xfer_request_denied(xfer); else if ((gaim_xfer_get_type(xfer) == GAIM_XFER_SEND) && * XXX - Sending a directory should be valid for some protocols. gaim_notify_error(NULL, NULL, _("Cannot send a directory."), NULL); gaim_xfer_request_denied(xfer); else if ((gaim_xfer_get_type(xfer) == GAIM_XFER_RECEIVE) && utf8 = g_filename_to_utf8(filename, -1, NULL, NULL, NULL); _("%s is not a regular file. Cowardly refusing to overwrite it.\n"), utf8); gaim_notify_error(NULL, NULL, msg, NULL); gaim_xfer_request_denied(xfer); gaim_xfer_request_accepted(xfer, filename); gaim_xfer_choose_file_cancel_cb(void *user_data, const char *filename) GaimXfer *xfer = (GaimXfer *)user_data; gaim_xfer_set_status(xfer, GAIM_XFER_STATUS_CANCEL_LOCAL); gaim_xfer_request_denied(xfer); gaim_xfer_choose_file(GaimXfer *xfer) gaim_request_file(xfer, NULL, gaim_xfer_get_filename(xfer), (gaim_xfer_get_type(xfer) == GAIM_XFER_RECEIVE), G_CALLBACK(gaim_xfer_choose_file_ok_cb), G_CALLBACK(gaim_xfer_choose_file_cancel_cb), xfer); cancel_recv_cb(GaimXfer *xfer) gaim_xfer_set_status(xfer, GAIM_XFER_STATUS_CANCEL_LOCAL); gaim_xfer_request_denied(xfer); gaim_xfer_ask_recv(GaimXfer *xfer) /* If we have already accepted the request, ask the destination file if (gaim_xfer_get_status(xfer) != GAIM_XFER_STATUS_ACCEPTED) { GaimBuddy *buddy = gaim_find_buddy(xfer->account, xfer->who); if (gaim_xfer_get_filename(xfer) != NULL) size = gaim_xfer_get_size(xfer); size_buf = gaim_str_size_to_units(size); buf = g_strdup_printf(_("%s wants to send you %s (%s)"), buddy ? gaim_buddy_get_alias(buddy) : xfer->who, gaim_xfer_get_filename(xfer), size_buf); buf = g_strdup_printf(_("%s wants to send you a file"), buddy ? gaim_buddy_get_alias(buddy) : xfer->who); if (xfer->message != NULL) serv_got_im(gaim_account_get_connection(xfer->account), xfer->who, xfer->message, 0, time(NULL)); gaim_request_accept_cancel(xfer, NULL, buf, NULL, GAIM_DEFAULT_ACTION_NONE, xfer, G_CALLBACK(gaim_xfer_choose_file), G_CALLBACK(cancel_recv_cb)); gaim_xfer_choose_file(xfer); ask_accept_ok(GaimXfer *xfer) gaim_xfer_request_accepted(xfer, NULL); ask_accept_cancel(GaimXfer *xfer) gaim_xfer_request_denied(xfer); gaim_xfer_ask_accept(GaimXfer *xfer) GaimBuddy *buddy = gaim_find_buddy(xfer->account, xfer->who); buf = g_strdup_printf(_("Accept file transfer request from %s?"), buddy ? gaim_buddy_get_alias(buddy) : xfer->who); if (gaim_xfer_get_remote_ip(xfer) && gaim_xfer_get_remote_port(xfer)) buf2 = g_strdup_printf(_("A file is available for download from:\n" "Remote host: %s\nRemote port: %d"), gaim_xfer_get_remote_ip(xfer), gaim_xfer_get_remote_port(xfer)); gaim_request_accept_cancel(xfer, NULL, buf, buf2, GAIM_DEFAULT_ACTION_NONE, xfer, G_CALLBACK(ask_accept_ok), G_CALLBACK(ask_accept_cancel)); gaim_xfer_request(GaimXfer *xfer) g_return_if_fail(xfer != NULL); g_return_if_fail(xfer->ops.init != NULL); if (gaim_xfer_get_type(xfer) == GAIM_XFER_RECEIVE) gaim_signal_emit(gaim_xfers_get_handle(), "file-recv-request", xfer); if (gaim_xfer_get_status(xfer) == GAIM_XFER_STATUS_CANCEL_LOCAL) /* The file-transfer was cancelled by a plugin */ gaim_xfer_cancel_local(xfer); else if (gaim_xfer_get_filename(xfer) || gaim_xfer_get_status(xfer) == GAIM_XFER_STATUS_ACCEPTED) message = g_strdup_printf(_("%s is offering to send file %s"), xfer->who, gaim_xfer_get_filename(xfer)); gaim_xfer_conversation_write(xfer, message, FALSE); /* Ask for a filename to save to if it's not already given by a plugin */ if (xfer->local_filename == NULL) gaim_xfer_ask_recv(xfer); gaim_xfer_ask_accept(xfer); gaim_xfer_choose_file(xfer); gaim_xfer_request_accepted(GaimXfer *xfer, const char *filename) type = gaim_xfer_get_type(xfer); account = gaim_xfer_get_account(xfer); if (!filename && type == GAIM_XFER_RECEIVE) { xfer->status = GAIM_XFER_STATUS_ACCEPTED; buddy = gaim_find_buddy(account, xfer->who); if (type == GAIM_XFER_SEND) { /* Check the filename. */ if (g_strrstr(filename, "../") || g_strrstr(filename, "..\\")) { if (g_strrstr(filename, "../")) { char *utf8 = g_filename_to_utf8(filename, -1, NULL, NULL, NULL); msg = g_strdup_printf(_("%s is not a valid filename.\n"), utf8); gaim_xfer_error(type, account, xfer->who, msg); if (g_stat(filename, &st) == -1) { gaim_xfer_show_file_error(xfer, filename); gaim_xfer_set_local_filename(xfer, filename); gaim_xfer_set_size(xfer, st.st_size); utf8 = g_filename_to_utf8(g_basename(filename), -1, NULL, NULL, NULL); gaim_xfer_set_filename(xfer, utf8); msg = g_strdup_printf(_("Offering to send %s to %s"), utf8, buddy ? gaim_buddy_get_alias(buddy) : xfer->who); gaim_xfer_conversation_write(xfer, msg, FALSE); xfer->status = GAIM_XFER_STATUS_ACCEPTED; gaim_xfer_set_local_filename(xfer, filename); msg = g_strdup_printf(_("Starting transfer of %s from %s"), xfer->filename, buddy ? gaim_buddy_get_alias(buddy) : xfer->who); gaim_xfer_conversation_write(xfer, msg, FALSE); gaim_xfer_request_denied(GaimXfer *xfer) g_return_if_fail(xfer != NULL); if (xfer->ops.request_denied != NULL) xfer->ops.request_denied(xfer); gaim_xfer_get_type(const GaimXfer *xfer) g_return_val_if_fail(xfer != NULL, GAIM_XFER_UNKNOWN); gaim_xfer_get_account(const GaimXfer *xfer) g_return_val_if_fail(xfer != NULL, NULL); gaim_xfer_get_status(const GaimXfer *xfer) g_return_val_if_fail(xfer != NULL, GAIM_XFER_STATUS_UNKNOWN); gaim_xfer_is_canceled(const GaimXfer *xfer) g_return_val_if_fail(xfer != NULL, TRUE); if ((gaim_xfer_get_status(xfer) == GAIM_XFER_STATUS_CANCEL_LOCAL) || (gaim_xfer_get_status(xfer) == GAIM_XFER_STATUS_CANCEL_REMOTE)) gaim_xfer_is_completed(const GaimXfer *xfer) g_return_val_if_fail(xfer != NULL, TRUE); return (gaim_xfer_get_status(xfer) == GAIM_XFER_STATUS_DONE); gaim_xfer_get_filename(const GaimXfer *xfer) g_return_val_if_fail(xfer != NULL, NULL); gaim_xfer_get_local_filename(const GaimXfer *xfer) g_return_val_if_fail(xfer != NULL, NULL); return xfer->local_filename; gaim_xfer_get_bytes_sent(const GaimXfer *xfer) g_return_val_if_fail(xfer != NULL, 0); gaim_xfer_get_bytes_remaining(const GaimXfer *xfer) g_return_val_if_fail(xfer != NULL, 0); return xfer->bytes_remaining; gaim_xfer_get_size(const GaimXfer *xfer) g_return_val_if_fail(xfer != NULL, 0); gaim_xfer_get_progress(const GaimXfer *xfer) g_return_val_if_fail(xfer != NULL, 0.0); if (gaim_xfer_get_size(xfer) == 0) return ((double)gaim_xfer_get_bytes_sent(xfer) / (double)gaim_xfer_get_size(xfer)); gaim_xfer_get_local_port(const GaimXfer *xfer) g_return_val_if_fail(xfer != NULL, -1); gaim_xfer_get_remote_ip(const GaimXfer *xfer) g_return_val_if_fail(xfer != NULL, NULL); gaim_xfer_get_remote_port(const GaimXfer *xfer) g_return_val_if_fail(xfer != NULL, -1); return xfer->remote_port; gaim_xfer_set_completed(GaimXfer *xfer, gboolean completed) g_return_if_fail(xfer != NULL); gaim_xfer_set_status(xfer, GAIM_XFER_STATUS_DONE); if (gaim_xfer_get_filename(xfer) != NULL) msg = g_strdup_printf(_("Transfer of file %s complete"), gaim_xfer_get_filename(xfer)); msg = g_strdup_printf(_("File transfer complete")); gaim_xfer_conversation_write(xfer, msg, FALSE); ui_ops = gaim_xfer_get_ui_ops(xfer); if (ui_ops != NULL && ui_ops->update_progress != NULL) ui_ops->update_progress(xfer, gaim_xfer_get_progress(xfer)); gaim_xfer_set_message(GaimXfer *xfer, const char *message) g_return_if_fail(xfer != NULL); xfer->message = g_strdup(message); gaim_xfer_set_filename(GaimXfer *xfer, const char *filename) g_return_if_fail(xfer != NULL); if (xfer->filename != NULL) xfer->filename = (filename == NULL ? NULL : g_strdup(filename)); gaim_xfer_set_local_filename(GaimXfer *xfer, const char *filename) g_return_if_fail(xfer != NULL); if (xfer->local_filename != NULL) g_free(xfer->local_filename); xfer->local_filename = (filename == NULL ? NULL : g_strdup(filename)); gaim_xfer_set_size(GaimXfer *xfer, size_t size) g_return_if_fail(xfer != NULL); xfer->bytes_remaining = size - xfer->bytes_sent; gaim_xfer_get_ui_ops(const GaimXfer *xfer) g_return_val_if_fail(xfer != NULL, NULL); gaim_xfer_set_init_fnc(GaimXfer *xfer, void (*fnc)(GaimXfer *)) g_return_if_fail(xfer != NULL); void gaim_xfer_set_request_denied_fnc(GaimXfer *xfer, void (*fnc)(GaimXfer *)) g_return_if_fail(xfer != NULL); xfer->ops.request_denied = fnc; gaim_xfer_set_read_fnc(GaimXfer *xfer, gssize (*fnc)(guchar **, GaimXfer *)) g_return_if_fail(xfer != NULL); gaim_xfer_set_write_fnc(GaimXfer *xfer, gssize (*fnc)(const guchar *, size_t, GaimXfer *)) g_return_if_fail(xfer != NULL); gaim_xfer_set_ack_fnc(GaimXfer *xfer, void (*fnc)(GaimXfer *, const guchar *, size_t)) g_return_if_fail(xfer != NULL); gaim_xfer_set_start_fnc(GaimXfer *xfer, void (*fnc)(GaimXfer *)) g_return_if_fail(xfer != NULL); gaim_xfer_set_end_fnc(GaimXfer *xfer, void (*fnc)(GaimXfer *)) g_return_if_fail(xfer != NULL); gaim_xfer_set_cancel_send_fnc(GaimXfer *xfer, void (*fnc)(GaimXfer *)) g_return_if_fail(xfer != NULL); xfer->ops.cancel_send = fnc; gaim_xfer_set_cancel_recv_fnc(GaimXfer *xfer, void (*fnc)(GaimXfer *)) g_return_if_fail(xfer != NULL); xfer->ops.cancel_recv = fnc; gaim_xfer_read(GaimXfer *xfer, guchar **buffer) g_return_val_if_fail(xfer != NULL, 0); g_return_val_if_fail(buffer != NULL, 0); if (gaim_xfer_get_size(xfer) == 0) s = MIN(gaim_xfer_get_bytes_remaining(xfer), 4096); if (xfer->ops.read != NULL) r = (xfer->ops.read)(buffer, xfer); r = read(xfer->fd, *buffer, s); if (r < 0 && errno == EAGAIN) else if ((gaim_xfer_get_size(xfer) > 0) && ((gaim_xfer_get_bytes_sent(xfer)+r) >= gaim_xfer_get_size(xfer))) gaim_xfer_set_completed(xfer, TRUE); gaim_xfer_write(GaimXfer *xfer, const guchar *buffer, size_t size) g_return_val_if_fail(xfer != NULL, 0); g_return_val_if_fail(buffer != NULL, 0); g_return_val_if_fail(size != 0, 0); s = MIN(gaim_xfer_get_bytes_remaining(xfer), size); if (xfer->ops.write != NULL) { r = (xfer->ops.write)(buffer, s, xfer); r = write(xfer->fd, buffer, s); if (r < 0 && errno == EAGAIN) if ((gaim_xfer_get_bytes_sent(xfer)+r) >= gaim_xfer_get_size(xfer)) gaim_xfer_set_completed(xfer, TRUE); transfer_cb(gpointer data, gint source, GaimInputCondition condition) GaimXfer *xfer = (GaimXfer *)data; if (condition & GAIM_INPUT_READ) { r = gaim_xfer_read(xfer, &buffer); fwrite(buffer, 1, r, xfer->dest_fp); gaim_xfer_cancel_remote(xfer); if (condition & GAIM_INPUT_WRITE) { size_t s = MIN(gaim_xfer_get_bytes_remaining(xfer), 4096); /* this is so the prpl can keep the connection open if it needs to for some odd reason. */ gaim_input_remove(xfer->watcher); fread(buffer, 1, s, xfer->dest_fp); /* Write as much as we're allowed to. */ r = gaim_xfer_write(xfer, buffer, s); gaim_xfer_cancel_remote(xfer); /* We have to seek back in the file now. */ fseek(xfer->dest_fp, r - s, SEEK_CUR); if (gaim_xfer_get_size(xfer) > 0) xfer->bytes_remaining -= r; if (xfer->ops.ack != NULL) xfer->ops.ack(xfer, buffer, r); ui_ops = gaim_xfer_get_ui_ops(xfer); if (ui_ops != NULL && ui_ops->update_progress != NULL) ui_ops->update_progress(xfer, gaim_xfer_get_progress(xfer)); if (gaim_xfer_is_completed(xfer)) begin_transfer(GaimXfer *xfer, GaimInputCondition cond) GaimXferType type = gaim_xfer_get_type(xfer); xfer->dest_fp = g_fopen(gaim_xfer_get_local_filename(xfer), type == GAIM_XFER_RECEIVE ? "wb" : "rb"); if (xfer->dest_fp == NULL) { gaim_xfer_show_file_error(xfer, gaim_xfer_get_local_filename(xfer)); gaim_xfer_cancel_local(xfer); xfer->watcher = gaim_input_add(xfer->fd, cond, transfer_cb, xfer); xfer->start_time = time(NULL); if (xfer->ops.start != NULL) connect_cb(gpointer data, gint source, GaimInputCondition condition) GaimXfer *xfer = (GaimXfer *)data; begin_transfer(xfer, condition); gaim_xfer_start(GaimXfer *xfer, int fd, const char *ip, g_return_if_fail(xfer != NULL); g_return_if_fail(gaim_xfer_get_type(xfer) != GAIM_XFER_UNKNOWN); type = gaim_xfer_get_type(xfer); xfer->bytes_remaining = gaim_xfer_get_size(xfer); gaim_xfer_set_status(xfer, GAIM_XFER_STATUS_STARTED); if (type == GAIM_XFER_RECEIVE) { xfer->remote_ip = g_strdup(ip); xfer->remote_port = port; /* Establish a file descriptor. */ gaim_proxy_connect(xfer->account, xfer->remote_ip, xfer->remote_port, connect_cb, xfer); begin_transfer(xfer, cond); gaim_xfer_end(GaimXfer *xfer) g_return_if_fail(xfer != NULL); /* See if we are actually trying to cancel this. */ if (!gaim_xfer_is_completed(xfer)) { gaim_xfer_cancel_local(xfer); xfer->end_time = time(NULL); if (xfer->ops.end != NULL) if (xfer->watcher != 0) { gaim_input_remove(xfer->watcher); if (xfer->dest_fp != NULL) { gaim_xfer_add(GaimXfer *xfer) g_return_if_fail(xfer != NULL); ui_ops = gaim_xfer_get_ui_ops(xfer); if (ui_ops != NULL && ui_ops->add_xfer != NULL) gaim_xfer_cancel_local(GaimXfer *xfer) g_return_if_fail(xfer != NULL); gaim_xfer_set_status(xfer, GAIM_XFER_STATUS_CANCEL_LOCAL); xfer->end_time = time(NULL); if (gaim_xfer_get_filename(xfer) != NULL) msg = g_strdup_printf(_("You canceled the transfer of %s"), gaim_xfer_get_filename(xfer)); msg = g_strdup_printf(_("File transfer cancelled")); gaim_xfer_conversation_write(xfer, msg, FALSE); if (gaim_xfer_get_type(xfer) == GAIM_XFER_SEND) if (xfer->ops.cancel_send != NULL) xfer->ops.cancel_send(xfer); if (xfer->ops.cancel_recv != NULL) xfer->ops.cancel_recv(xfer); if (xfer->watcher != 0) { gaim_input_remove(xfer->watcher); if (xfer->dest_fp != NULL) { ui_ops = gaim_xfer_get_ui_ops(xfer); if (ui_ops != NULL && ui_ops->cancel_local != NULL) ui_ops->cancel_local(xfer); xfer->bytes_remaining = 0; gaim_xfer_cancel_remote(GaimXfer *xfer) g_return_if_fail(xfer != NULL); gaim_request_close_with_handle(xfer); gaim_xfer_set_status(xfer, GAIM_XFER_STATUS_CANCEL_REMOTE); xfer->end_time = time(NULL); account = gaim_xfer_get_account(xfer); buddy = gaim_find_buddy(account, xfer->who); if (gaim_xfer_get_filename(xfer) != NULL) msg = g_strdup_printf(_("%s canceled the transfer of %s"), buddy ? gaim_buddy_get_alias(buddy) : xfer->who, gaim_xfer_get_filename(xfer)); msg = g_strdup_printf(_("%s canceled the file transfer"), buddy ? gaim_buddy_get_alias(buddy) : xfer->who); gaim_xfer_conversation_write(xfer, msg, TRUE); gaim_xfer_error(gaim_xfer_get_type(xfer), account, xfer->who, msg); if (gaim_xfer_get_type(xfer) == GAIM_XFER_SEND) if (xfer->ops.cancel_send != NULL) xfer->ops.cancel_send(xfer); if (xfer->ops.cancel_recv != NULL) xfer->ops.cancel_recv(xfer); if (xfer->watcher != 0) { gaim_input_remove(xfer->watcher); if (xfer->dest_fp != NULL) { ui_ops = gaim_xfer_get_ui_ops(xfer); if (ui_ops != NULL && ui_ops->cancel_remote != NULL) ui_ops->cancel_remote(xfer); xfer->bytes_remaining = 0; gaim_xfer_error(GaimXferType type, GaimAccount *account, const char *who, const char *msg) g_return_if_fail(msg != NULL); g_return_if_fail(type != GAIM_XFER_UNKNOWN); buddy = gaim_find_buddy(account, who); who = gaim_buddy_get_alias(buddy); if (type == GAIM_XFER_SEND) title = g_strdup_printf(_("File transfer to %s failed."), who); title = g_strdup_printf(_("File transfer from %s failed."), who); gaim_notify_error(NULL, NULL, title, msg); gaim_xfer_update_progress(GaimXfer *xfer) g_return_if_fail(xfer != NULL); ui_ops = gaim_xfer_get_ui_ops(xfer); if (ui_ops != NULL && ui_ops->update_progress != NULL) ui_ops->update_progress(xfer, gaim_xfer_get_progress(xfer)); /************************************************************************** * File Transfer Subsystem API **************************************************************************/ gaim_xfers_get_handle(void) { void *handle = gaim_xfers_get_handle(); gaim_signal_register(handle, "file-recv-accept", gaim_marshal_VOID__POINTER, gaim_value_new(GAIM_TYPE_SUBTYPE, GAIM_SUBTYPE_XFER)); gaim_signal_register(handle, "file-send-accept", gaim_marshal_VOID__POINTER, gaim_value_new(GAIM_TYPE_POINTER, GAIM_SUBTYPE_XFER)); gaim_signal_register(handle, "file-recv-start", gaim_marshal_VOID__POINTER, gaim_value_new(GAIM_TYPE_POINTER, GAIM_SUBTYPE_XFER)); gaim_signal_register(handle, "file-send-start", gaim_marshal_VOID__POINTER, gaim_value_new(GAIM_TYPE_POINTER, GAIM_SUBTYPE_XFER)); gaim_signal_register(handle, "file-send-cancel", gaim_marshal_VOID__POINTER, gaim_value_new(GAIM_TYPE_POINTER, GAIM_SUBTYPE_XFER)); gaim_signal_register(handle, "file-recv-cancel", gaim_marshal_VOID__POINTER, gaim_value_new(GAIM_TYPE_POINTER, GAIM_SUBTYPE_XFER)); gaim_signal_register(handle, "file-send-complete", gaim_marshal_VOID__POINTER, gaim_value_new(GAIM_TYPE_POINTER, GAIM_SUBTYPE_XFER)); gaim_signal_register(handle, "file-recv-complete", gaim_marshal_VOID__POINTER, gaim_value_new(GAIM_TYPE_POINTER, GAIM_SUBTYPE_XFER)); gaim_signal_register(handle, "file-recv-request", gaim_marshal_VOID__POINTER, gaim_value_new(GAIM_TYPE_POINTER, GAIM_SUBTYPE_XFER)); gaim_xfers_uninit(void) { gaim_signals_disconnect_by_handle(gaim_xfers_get_handle()); gaim_xfers_set_ui_ops(GaimXferUiOps *ops) { gaim_xfers_get_ui_ops(void) {