Mercurial > pidgin
changeset 7805:5f0bb52c0609
[gaim-migrate @ 8452]
File transfer changes from marv.
This fixes various ft related bugs, including:
* Sometimes clicking cancel on a send would crash.
* We seemed to leak the GaimXfer most of the time.
* Choosing to not overwrite the file would cancel the
receive altogether.
This should fix all these issues. It would be nice if
someone (SimGuy?) could test this for me, especially on
windows, to make sure i didn't break anything. Jabber
ft is untested, althoughi didn't make any changes in
the jabber source. So, it should still work, i just
can't comfirm it. Yahoo and OSCAR do still work.
Amoung other things, this patch impliments some
reference counting on the GaimXfer, so the ui can keep
it around a while if it wants, without leaking it
because we're afraid to destroy it.
committer: Tailor Script <tailor@pidgin.im>
author | Mark Doliner <mark@kingant.net> |
---|---|
date | Mon, 08 Dec 2003 04:58:07 +0000 |
parents | 622c9149609c |
children | c86f075b269a |
files | src/ft.c src/ft.h src/gtkft.c src/protocols/oscar/oscar.c src/protocols/yahoo/yahoo_filexfer.c |
diffstat | 5 files changed, 169 insertions(+), 79 deletions(-) [+] |
line wrap: on
line diff
--- a/src/ft.c Mon Dec 08 03:51:33 2003 +0000 +++ b/src/ft.c Mon Dec 08 04:58:07 2003 +0000 @@ -40,6 +40,7 @@ xfer = g_new0(GaimXfer, 1); + xfer->ref = 1; xfer->type = type; xfer->account = account; xfer->who = g_strdup(who); @@ -55,14 +56,14 @@ return xfer; } -void +static void gaim_xfer_destroy(GaimXfer *xfer) { GaimXferUiOps *ui_ops; g_return_if_fail(xfer != NULL); - if (!xfer->completed) + if (gaim_xfer_get_status(xfer) == GAIM_XFER_STATUS_STARTED) gaim_xfer_cancel_local(xfer); ui_ops = gaim_xfer_get_ui_ops(xfer); @@ -83,6 +84,25 @@ } void +gaim_xfer_ref(GaimXfer *xfer) +{ + g_return_if_fail(xfer != NULL); + + xfer->ref++; +} + +void +gaim_xfer_unref(GaimXfer *xfer) +{ + g_return_if_fail(xfer != NULL); + + xfer->ref--; + + if (xfer->ref == 0) + gaim_xfer_destroy(xfer); +} + +void gaim_xfer_request(GaimXfer *xfer) { GaimXferUiOps *ui_ops; @@ -99,15 +119,11 @@ } void -gaim_xfer_request_accepted(GaimXfer *xfer, char *filename) +gaim_xfer_request_accepted(GaimXfer *xfer, const char *filename) { GaimXferType type; if (xfer == NULL || filename == NULL) { - - if (filename != NULL) - g_free(filename); - return; } @@ -126,8 +142,7 @@ gaim_xfer_error(type, xfer->who, msg); g_free(msg); - g_free(filename); - + gaim_xfer_unref(xfer); return; } @@ -139,8 +154,7 @@ gaim_xfer_error(type, xfer->who, msg); g_free(msg); - g_free(filename); - + gaim_xfer_unref(xfer); return; } @@ -153,7 +167,6 @@ gaim_xfer_set_local_filename(xfer, filename); } - g_free(filename); xfer->ops.init(xfer); } @@ -163,9 +176,10 @@ { g_return_if_fail(xfer != NULL); - gaim_xfer_destroy(xfer); + if (xfer->ops.request_denied != NULL) + xfer->ops.request_denied(xfer); - /* TODO */ + gaim_xfer_unref(xfer); } GaimXferType @@ -184,12 +198,24 @@ return xfer->account; } -GaimXferCancelType +GaimXferStatusType +gaim_xfer_get_status(const GaimXfer *xfer) +{ + g_return_val_if_fail(xfer != NULL, GAIM_XFER_STATUS_UNKNOWN); + + return xfer->status; +} + +gboolean gaim_xfer_is_canceled(const GaimXfer *xfer) { g_return_val_if_fail(xfer != NULL, TRUE); - return xfer->canceled; + if ((gaim_xfer_get_status(xfer) == GAIM_XFER_STATUS_CANCEL_LOCAL) || + (gaim_xfer_get_status(xfer) == GAIM_XFER_STATUS_CANCEL_REMOTE)) + return TRUE; + else + return FALSE; } gboolean @@ -197,7 +223,7 @@ { g_return_val_if_fail(xfer != NULL, TRUE); - return xfer->completed; + return (gaim_xfer_get_status(xfer) == GAIM_XFER_STATUS_DONE); } const char * @@ -285,11 +311,11 @@ } static void -gaim_xfer_set_canceled(GaimXfer *xfer, GaimXferCancelType canceled) +gaim_xfer_set_status(GaimXfer *xfer, GaimXferStatusType status) { g_return_if_fail(xfer != NULL); - xfer->canceled = canceled; + xfer->status = status; } void @@ -299,7 +325,8 @@ g_return_if_fail(xfer != NULL); - xfer->completed = completed; + if (completed == TRUE) + gaim_xfer_set_status(xfer, GAIM_XFER_STATUS_DONE); ui_ops = gaim_xfer_get_ui_ops(xfer); @@ -357,6 +384,12 @@ xfer->ops.init = fnc; } +void gaim_xfer_set_request_denied_fnc(GaimXfer *xfer, void (*fnc)(GaimXfer *)) +{ + g_return_if_fail(xfer != NULL); + + xfer->ops.request_denied = fnc; +} void gaim_xfer_set_read_fnc(GaimXfer *xfer, size_t (*fnc)(char **, GaimXfer *)) @@ -565,6 +598,8 @@ xfer->bytes_remaining = gaim_xfer_get_size(xfer); xfer->bytes_sent = 0; + gaim_xfer_set_status(xfer, GAIM_XFER_STATUS_STARTED); + if (type == GAIM_XFER_RECEIVE) { cond = GAIM_INPUT_READ; @@ -597,7 +632,7 @@ g_return_if_fail(xfer != NULL); /* See if we are actually trying to cancel this. */ - if (!xfer->completed) { + if (gaim_xfer_get_status(xfer) != GAIM_XFER_STATUS_DONE) { gaim_xfer_cancel_local(xfer); return; } @@ -617,6 +652,8 @@ fclose(xfer->dest_fp); xfer->dest_fp = NULL; } + + gaim_xfer_unref(xfer); } void @@ -626,7 +663,7 @@ g_return_if_fail(xfer != NULL); - gaim_xfer_set_canceled(xfer, GAIM_XFER_CANCEL_LOCAL); + gaim_xfer_set_status(xfer, GAIM_XFER_STATUS_CANCEL_LOCAL); if (gaim_xfer_get_type(xfer) == GAIM_XFER_SEND) { @@ -658,6 +695,8 @@ ui_ops->cancel_local(xfer); xfer->bytes_remaining = 0; + + gaim_xfer_unref(xfer); } void @@ -667,7 +706,7 @@ g_return_if_fail(xfer != NULL); - gaim_xfer_set_canceled(xfer, GAIM_XFER_CANCEL_REMOTE); + gaim_xfer_set_status(xfer, GAIM_XFER_STATUS_CANCEL_REMOTE); if (gaim_xfer_get_type(xfer) == GAIM_XFER_SEND) { @@ -699,6 +738,8 @@ ui_ops->cancel_remote(xfer); xfer->bytes_remaining = 0; + + gaim_xfer_unref(xfer); } void
--- a/src/ft.h Mon Dec 08 03:51:33 2003 +0000 +++ b/src/ft.h Mon Dec 08 04:58:07 2003 +0000 @@ -41,12 +41,18 @@ } GaimXferType; +/** + * The different states of the xfer. + */ typedef enum { - GAIM_XFER_CANCEL_NOT = 0, - GAIM_XFER_CANCEL_LOCAL, - GAIM_XFER_CANCEL_REMOTE -} GaimXferCancelType; + GAIM_XFER_STATUS_UNKNOWN = 0, /**< Unknown, the xfer may be null. */ + GAIM_XFER_STATUS_NOT_STARTED, /**< It hasn't started yet. */ + GAIM_XFER_STATUS_STARTED, /**< gaim_xfer_start has been called. */ + GAIM_XFER_STATUS_DONE, /**< The xfer completed successfully. */ + GAIM_XFER_STATUS_CANCEL_LOCAL, /**< The xfer was canceled by us. */ + GAIM_XFER_STATUS_CANCEL_REMOTE /**< The xfer was canceled by the other end, or we couldn't connect. */ +} GaimXferStatusType; /** * File transfer UI operations. @@ -72,6 +78,7 @@ */ struct _GaimXfer { + guint ref; /**<The reference count. */ GaimXferType type; /**< The type of transfer. */ GaimAccount *account; /**< The account. */ @@ -96,13 +103,13 @@ size_t bytes_sent; /**< The number of bytes sent. */ size_t bytes_remaining; /**< The number of bytes remaining. */ - GaimXferCancelType canceled; /**< File Transfer is canceled. */ - gboolean completed; /**< File Transfer is completed. */ + GaimXferStatusType status; /**< File Transfer's status. */ /* I/O operations. */ struct { void (*init)(GaimXfer *xfer); + void (*request_denied)(GaimXfer *xfer); void (*start)(GaimXfer *xfer); void (*end)(GaimXfer *xfer); void (*cancel_send)(GaimXfer *xfer); @@ -130,6 +137,10 @@ /** * Creates a new file transfer handle. + * This is called by prpls. + * The handle starts with a ref count of 1, and this reference + * is owned by the core. The prpl normally does not need to + * gaim_xfer_ref or unref. * * @param account The account sending or receiving the file. * @param type The type of file transfer. @@ -141,11 +152,23 @@ GaimXferType type, const char *who); /** - * Destroys a file transfer handle. + * Increases the reference count on a GaimXfer. + * Please call gaim_xfer_unref later. * - * @param xfer The file transfer to destroy. + * @param xfer A file transfer handle. */ -void gaim_xfer_destroy(GaimXfer *xfer); +void gaim_xfer_ref(GaimXfer *xfer); + +/** + * Decreases the reference count on a GaimXfer. + * If the reference reaches 0, gaim_xfer_destroy (an internal function) + * will destroy the xfer. It calls the ui destroy cb first. + * Since the core keeps a ref on the xfer, only an erronous call to + * this function will destroy the xfer while still in use. + * + * @param xfer A file transfer handle. + */ +void gaim_xfer_unref(GaimXfer *xfer); /** * Requests confirmation for a file transfer from the user. @@ -160,7 +183,7 @@ * @param xfer The file transfer. * @param filename The filename. */ -void gaim_xfer_request_accepted(GaimXfer *xfer, char *filename); +void gaim_xfer_request_accepted(GaimXfer *xfer, const char *filename); /** * Called if the user rejects the file transfer request. @@ -188,13 +211,22 @@ GaimAccount *gaim_xfer_get_account(const GaimXfer *xfer); /** + * Returns the status of the xfer. + * + * @param xfer The file transfer. + * + * @return The status. + */ +GaimXferStatusType gaim_xfer_get_status(const GaimXfer *xfer); + +/** * Returns true if the file transfer was canceled. * * @param xfer The file transfer. * * @return Whether or not the transfer was canceled. */ -GaimXferCancelType gaim_xfer_is_canceled(const GaimXfer *xfer); +gboolean gaim_xfer_is_canceled(const GaimXfer *xfer); /** * Returns the completed state for a file transfer. @@ -245,7 +277,7 @@ * Returns the size of the file being sent or received. * * @param xfer The file transfer. - * + * * @return The total size of the file. */ size_t gaim_xfer_get_size(const GaimXfer *xfer); @@ -366,6 +398,14 @@ void (*fnc)(GaimXfer *, const char *, size_t)); /** + * Sets the function to be called if the request is denied. + * + * @param xfer The file transfer. + * @param fnc The request denied prpl callback. + */ +void gaim_xfer_set_request_denied_fnc(GaimXfer *xfer, void (*fnc)(GaimXfer *)); + +/** * Sets the transfer initialization function for the file transfer. * * This function is required, and must call gaim_xfer_start() with
--- a/src/gtkft.c Mon Dec 08 03:51:33 2003 +0000 +++ b/src/gtkft.c Mon Dec 08 04:58:07 2003 +0000 @@ -80,6 +80,7 @@ GtkWidget *filesel; GtkTreeIter iter; time_t start_time; + gboolean in_list; char *name; @@ -98,6 +99,12 @@ NUM_COLUMNS }; + +/************************************************************************** + * Prototype(s) + **************************************************************************/ +static int choose_file(GaimXfer *xfer); + /************************************************************************** * Utility Functions **************************************************************************/ @@ -766,7 +773,10 @@ g_return_if_fail(dialog != NULL); g_return_if_fail(xfer != NULL); + gaim_xfer_ref(xfer); + data = GAIM_GTKXFER(xfer); + data->in_list = TRUE; gaim_gtkxfer_dialog_show(dialog); @@ -823,12 +833,12 @@ if (data == NULL) return; - gtk_list_store_remove(GTK_LIST_STORE(dialog->model), &data->iter); + if (!data->in_list) + return; - g_free(data->name); - g_free(data); + data->in_list = FALSE; - xfer->ui_data = NULL; + gtk_list_store_remove(GTK_LIST_STORE(dialog->model), &data->iter); dialog->num_transfers--; @@ -836,6 +846,8 @@ gaim_gtkxfer_dialog_hide(dialog); else ensure_row_selected(dialog); + + gaim_xfer_unref(xfer); } void @@ -846,7 +858,6 @@ GdkPixbuf *pixbuf; gchar *status; - g_return_if_fail(dialog != NULL); g_return_if_fail(xfer != NULL); @@ -856,19 +867,8 @@ return; - if ((gaim_xfer_is_canceled(xfer) == GAIM_XFER_CANCEL_LOCAL) && (dialog->auto_clear)) { - gtk_list_store_remove(GTK_LIST_STORE(dialog->model), &data->iter); - - g_free(data->name); - g_free(data); - - xfer->ui_data = NULL; - - dialog->num_transfers--; - - if (dialog->num_transfers == 0 && !dialog->keep_open) - gaim_gtkxfer_dialog_hide(dialog); - + if ((gaim_xfer_get_status(xfer) == GAIM_XFER_STATUS_CANCEL_LOCAL) && (dialog->auto_clear)) { + gaim_gtkxfer_dialog_remove_xfer(dialog, xfer); return; } @@ -878,7 +878,7 @@ GAIM_STOCK_FILE_CANCELED, GTK_ICON_SIZE_MENU, NULL); - if (gaim_xfer_is_canceled(xfer) == GAIM_XFER_CANCEL_LOCAL) + if (gaim_xfer_get_status(xfer) == GAIM_XFER_STATUS_CANCEL_LOCAL) status = _("Canceled"); else status = _("Failed"); @@ -907,6 +907,9 @@ if ((data = GAIM_GTKXFER(xfer)) == NULL) return; + if (data->in_list == FALSE) + return; + size_str = gaim_str_size_to_units(gaim_xfer_get_size(xfer)); remaining_str = gaim_str_size_to_units(gaim_xfer_get_bytes_remaining(xfer)); @@ -958,13 +961,26 @@ static void gaim_gtkxfer_destroy(GaimXfer *xfer) { - gaim_gtkxfer_dialog_remove_xfer(xfer_dialog, xfer); + GaimGtkXferUiData *data; + + data = GAIM_GTKXFER(xfer); + if (data) { + if (data->name) + g_free(data->name); + g_free(data); + xfer->ui_data = NULL; + } } static gboolean choose_file_close_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data) { - gaim_xfer_request_denied((GaimXfer *)user_data); + GaimXfer *xfer = (GaimXfer *)user_data; + GaimGtkXferUiData *data; + + data = GAIM_GTKXFER(xfer); + data->filesel = NULL; + gaim_xfer_request_denied(xfer); return FALSE; } @@ -976,11 +992,9 @@ GaimGtkXferUiData *data; data = GAIM_GTKXFER(xfer); - - gaim_xfer_request_denied(xfer); - gtk_widget_destroy(data->filesel); data->filesel = NULL; + gaim_xfer_request_denied(xfer); } static int @@ -992,10 +1006,7 @@ gaim_xfer_request_accepted(xfer, data->name); - /* - * No, we don't want to free data->name. gaim_xfer_request_accepted - * will deal with it. - */ + g_free(data->name); data->name = NULL; return 0; @@ -1011,7 +1022,7 @@ g_free(data->name); data->name = NULL; - gaim_xfer_request_denied(xfer); + choose_file(xfer); return 0; } @@ -1068,7 +1079,7 @@ G_CALLBACK(dont_overwrite_cb)); } else { - gaim_xfer_request_accepted(xfer, g_strdup(name)); + gaim_xfer_request_accepted(xfer, name); } } @@ -1174,25 +1185,20 @@ gaim_gtkxfer_update_progress(GaimXfer *xfer, double percent) { gaim_gtkxfer_dialog_update_xfer(xfer_dialog, xfer); - - /* See if it's removed. */ - /* XXX - This caused some bad stuff, and I don't see a point to it */ -#if 0 - if (xfer->ui_data == NULL) - gaim_xfer_destroy(xfer); -#endif } static void gaim_gtkxfer_cancel_local(GaimXfer *xfer) { - gaim_gtkxfer_dialog_cancel_xfer(xfer_dialog, xfer); + if (xfer_dialog) + gaim_gtkxfer_dialog_cancel_xfer(xfer_dialog, xfer); } static void gaim_gtkxfer_cancel_remote(GaimXfer *xfer) { - gaim_gtkxfer_dialog_cancel_xfer(xfer_dialog, xfer); + if (xfer_dialog) + gaim_gtkxfer_dialog_cancel_xfer(xfer_dialog, xfer); } static GaimXferUiOps ops =
--- a/src/protocols/oscar/oscar.c Mon Dec 08 03:51:33 2003 +0000 +++ b/src/protocols/oscar/oscar.c Mon Dec 08 04:58:07 2003 +0000 @@ -717,7 +717,7 @@ while (od->file_transfers) { GaimXfer *xfer; xfer = (GaimXfer *)od->file_transfers->data; - gaim_xfer_destroy(xfer); + gaim_xfer_cancel_local(xfer); } while (od->requesticon) { char *sn = od->requesticon->data;
--- a/src/protocols/yahoo/yahoo_filexfer.c Mon Dec 08 03:51:33 2003 +0000 +++ b/src/protocols/yahoo/yahoo_filexfer.c Mon Dec 08 04:58:07 2003 +0000 @@ -68,7 +68,7 @@ return; if (source < 0) { gaim_xfer_error(GAIM_XFER_RECEIVE, xfer->who, _("Unable to connect.")); - gaim_xfer_end(xfer); + gaim_xfer_cancel_remote(xfer); return; } @@ -122,6 +122,7 @@ GaimConnection *gc; GaimAccount *account; struct yahoo_data *yd; + char *filename; gaim_debug(GAIM_DEBUG_INFO, "yahoo", "AAA - in yahoo_sendfile_connected\n"); @@ -138,7 +139,7 @@ if (source < 0) { gaim_xfer_error(GAIM_XFER_RECEIVE, xfer->who, _("Unable to connect.")); - gaim_xfer_end(xfer); + gaim_xfer_cancel_remote(xfer); return; } @@ -153,7 +154,8 @@ yahoo_packet_hash(pkt, 0, gaim_connection_get_display_name(gc)); yahoo_packet_hash(pkt, 5, xfer->who); yahoo_packet_hash(pkt, 14, ""); - yahoo_packet_hash(pkt, 27, gaim_xfer_get_local_filename(xfer)); + filename = g_path_get_basename(gaim_xfer_get_local_filename(xfer)); + yahoo_packet_hash(pkt, 27, filename); yahoo_packet_hash(pkt, 28, size); content_length = YAHOO_PACKET_HDRLEN + yahoo_packet_length(pkt); @@ -179,6 +181,7 @@ g_free(size); g_free(post); g_free(buf); + g_free(filename); } static void yahoo_xfer_init(GaimXfer *xfer) @@ -517,5 +520,5 @@ gaim_xfer_set_write_fnc(xfer, yahoo_xfer_write); /* Now perform the request */ - gaim_xfer_request_accepted(xfer, g_strdup(file)); + gaim_xfer_request_accepted(xfer, file); }