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);
 }